diff options
author | Karl Berry <karl@freefriends.org> | 2013-04-07 18:19:31 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2013-04-07 18:19:31 +0000 |
commit | 752012c605d34cd943795527a9738475a6958fcc (patch) | |
tree | 4ee06acdd8333a662c2d6f6ef716235053468f55 /Master/texmf-dist/asymptote/geometry.asy | |
parent | 9789d09132f18a838e84f041b4b3aff28d3426ec (diff) |
texmf -> texmf-dist: start with unique dirs from texmf
git-svn-id: svn://tug.org/texlive/trunk@29712 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/asymptote/geometry.asy')
-rw-r--r-- | Master/texmf-dist/asymptote/geometry.asy | 7192 |
1 files changed, 7192 insertions, 0 deletions
diff --git a/Master/texmf-dist/asymptote/geometry.asy b/Master/texmf-dist/asymptote/geometry.asy new file mode 100644 index 00000000000..bbbc823fc54 --- /dev/null +++ b/Master/texmf-dist/asymptote/geometry.asy @@ -0,0 +1,7192 @@ +// geometry.asy + +// Copyright (C) 2007 +// Author: Philippe IVALDI 2007/09/01 +// http://www.piprime.fr/ + +// This program is free software ; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation ; either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY ; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public License +// along with this program ; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +// COMMENTARY: +// An Asymptote geometry module. + +// THANKS: +// Special thanks to Olivier Guibé for his help in mathematical issues. + +// BUGS: + +// CODE: + +import math; +import markers; + +// A rotation in the direction dir limited to [-90,90] +// This is useful for rotating text along a line in the direction dir. +private transform rotate(explicit pair dir) +{ + real angle=degrees(dir); + if(angle > 90 && angle < 270) angle -= 180; + return rotate(angle); +} + +// *=======================================================* +// *........................HEADER.........................* +/*<asyxml><variable type="real" signature="epsgeo"><code></asyxml>*/ +real epsgeo = 10 * sqrt(realEpsilon);/*<asyxml></code><documentation>Variable used in the approximate calculations.</documentation></variable></asyxml>*/ + +/*<asyxml><function type="void" signature="addMargins(picture,real,real,real,real)"><code></asyxml>*/ +void addMargins(picture pic = currentpicture, + real lmargin = 0, real bmargin = 0, + real rmargin = lmargin, real tmargin = bmargin, + bool rigid = true, bool allObject = true) +{/*<asyxml></code><documentation>Add margins to 'pic' with respect to + the current bounding box of 'pic'. + If 'rigid' is false, margins are added iff an infinite curve will + be prolonged on the margin. + If 'allObject' is false, fixed - size objects (such as labels and + arrowheads) will be ignored.</documentation></function></asyxml>*/ + pair m = allObject ? truepoint(pic, SW) : point(pic, SW); + pair M = allObject ? truepoint(pic, NE) : point(pic, NE); + if(rigid) { + draw(m - inverse(pic.calculateTransform()) * (lmargin, bmargin), invisible); + draw(M + inverse(pic.calculateTransform()) * (rmargin, tmargin), invisible); + } else pic.addBox(m, M, -(lmargin, bmargin), (rmargin, tmargin)); +} + +real approximate(real t) +{ + real ot = t; + if(abs(t - ceil(t)) < epsgeo) ot = ceil(t); + else if(abs(t - floor(t)) < epsgeo) ot = floor(t); + return ot; +} + +real[] approximate(real[] T) +{ + return map(approximate, T); +} + +/*<asyxml><function type="real" signature="binomial(real,real)"><code></asyxml>*/ +real binomial(real n, real k) +{/*<asyxml></code><documentation>Return n!/((n - k)!*k!)</documentation></function></asyxml>*/ + return gamma(n + 1)/(gamma(n - k + 1) * gamma(k + 1)); +} + +/*<asyxml><function type="real" signature="rf(real,real,real)"><code></asyxml>*/ +real rf(real x, real y, real z) +{/*<asyxml></code><documentation>Computes Carlson's elliptic integral of the first kind. + x, y, and z must be non negative, and at most one can be zero.</documentation></function></asyxml>*/ + real ERRTOL = 0.0025, + TINY = 1.5e-38, + BIG = 3e37, + THIRD = 1/3, + C1 = 1/24, + C2 = 0.1, + C3 = 3/44, + C4 = 1/14; + real alamb, ave, delx, dely, delz, e2, e3, sqrtx, sqrty, sqrtz, xt, yt, zt; + if(min(x, y, z) < 0 || min(x + y, x + z, y + z) < TINY || + max(x, y, z) > BIG) abort("rf: invalid arguments."); + xt = x; + yt = y; + zt = z; + do { + sqrtx = sqrt(xt); + sqrty = sqrt(yt); + sqrtz = sqrt(zt); + alamb = sqrtx * (sqrty + sqrtz) + sqrty * sqrtz; + xt = 0.25 * (xt + alamb); + yt = 0.25 * (yt + alamb); + zt = 0.25 * (zt + alamb); + ave = THIRD * (xt + yt + zt); + delx = (ave - xt)/ave; + dely = (ave - yt)/ave; + delz = (ave - zt)/ave; + } while(max(fabs(delx), fabs(dely), fabs(delz)) > ERRTOL); + e2 = delx * dely - delz * delz; + e3 = delx * dely * delz; + return (1.0 + (C1 * e2 - C2 - C3 * e3) * e2 + C4 * e3)/sqrt(ave); +} + +/*<asyxml><function type="real" signature="rd(real,real,real)"><code></asyxml>*/ +real rd(real x, real y, real z) +{/*<asyxml></code><documentation>Computes Carlson's elliptic integral of the second kind. + x and y must be positive, and at most one can be zero. + z must be non negative.</documentation></function></asyxml>*/ + real ERRTOL = 0.0015, + TINY = 1e-25, + BIG = 4.5 * 10.0^21, + C1 = (3/14), + C2 = (1/6), + C3 = (9/22), + C4 = (3/26), + C5 = (0.25 * C3), + C6 = (1.5 * C4); + real alamb, ave, delx, dely, delz, ea, eb, ec, ed, ee, fac, sqrtx, sqrty, + sqrtz, sum, xt, yt, zt; + if (min(x, y) < 0 || min(x + y, z) < TINY || max(x, y, z) > BIG) + abort("rd: invalid arguments"); + xt = x; + yt = y; + zt = z; + sum = 0; + fac = 1; + do { + sqrtx = sqrt(xt); + sqrty = sqrt(yt); + sqrtz = sqrt(zt); + alamb = sqrtx * (sqrty + sqrtz) + sqrty * sqrtz; + sum += fac/(sqrtz * (zt + alamb)); + fac = 0.25 * fac; + xt = 0.25 * (xt + alamb); + yt = 0.25 * (yt + alamb); + zt = 0.25 * (zt + alamb); + ave = 0.2 * (xt + yt + 3.0 * zt); + delx = (ave - xt)/ave; + dely = (ave - yt)/ave; + delz = (ave - zt)/ave; + } while (max(fabs(delx), fabs(dely), fabs(delz)) > ERRTOL); + ea = delx * dely; + eb = delz * delz; + ec = ea - eb; + ed = ea - 6 * eb; + ee = ed + ec + ec; + return 3 * sum + fac * (1.0 + ed * (-C1 + C5 * ed - C6 * delz * ee) + +delz * (C2 * ee + delz * (-C3 * ec + delz * C4 * ea)))/(ave * sqrt(ave)); +} + +/*<asyxml><function type="real" signature="elle(real,real)"><code></asyxml>*/ +real elle(real phi, real k) +{/*<asyxml></code><documentation>Legendre elliptic integral of the 2nd kind, + evaluated using Carlson's functions RD and RF. + The argument ranges are -infinity < phi < +infinity, 0 <= k * sin(phi) <= 1.</documentation></function></asyxml>*/ + real result; + if (phi >= 0 && phi <= pi/2) { + real cc, q, s; + s = sin(phi); + cc = cos(phi)^2; + q = (1 - s * k) * (1 + s * k); + result = s * (rf(cc, q, 1) - (s * k)^2 * rd(cc, q, 1)/3); + } else + if (phi <= pi && phi >= 0) { + result = 2 * elle(pi/2, k) - elle(pi - phi, k); + } else + if (phi <= 3 * pi/2 && phi >= 0) { + result = 2 * elle(pi/2, k) + elle(phi - pi, k); + } else + if (phi <= 2 * pi && phi >= 0) { + result = 4 * elle(pi/2, k) - elle(2 * pi - phi, k); + } else + if (phi >= 0) { + int nb = floor(0.5 * phi/pi); + result = nb * elle(2 * pi, k) + elle(phi%(2 * pi), k); + } else result = -elle(-phi, k); + return result; +} + +/*<asyxml><function type="pair[]" signature="intersectionpoints(pair,pair,real,real,real,real,real,real)"><code></asyxml>*/ +pair[] intersectionpoints(pair A, pair B, + real a, real b, real c, real d, real f, real g) +{/*<asyxml></code><documentation>Intersection points with the line (AB) and the quadric curve + a * x^2 + b * x * y + c * y^2 + d * x + f * y + g = 0 given in the default coordinate system</documentation></function></asyxml>*/ + pair[] op; + real ap = B.y - A.y, + bpp = A.x - B.x, + cp = A.y * B.x - A.x * B.y; + real sol[]; + if (abs(ap) > epsgeo) { + real aa = ap * c + a * bpp^2/ap - b * bpp, + bb = ap * f - bpp * d + 2 * a * bpp * cp/ap - b * cp, + cc = ap * g - cp * d + a * cp^2/ap; + sol = quadraticroots(aa, bb, cc); + for (int i = 0; i < sol.length; ++i) { + op.push((-bpp * sol[i]/ap - cp/ap, sol[i])); + } + } else { + real aa = a * bpp, + bb = d * bpp - b * cp, + cc = g * bpp - cp * f + c * cp^2/bpp; + sol = quadraticroots(aa, bb, cc); + for (int i = 0; i < sol.length; ++i) { + op.push((sol[i], -cp/bpp)); + } + } + return op; +} + +/*<asyxml><function type="pair[]" signature="intersectionpoints(pair,pair,real[])"><code></asyxml>*/ +pair[] intersectionpoints(pair A, pair B, real[] equation) +{/*<asyxml></code><documentation>Return the intersection points of the line AB with + the conic whose an equation is + equation[0] * x^2 + equation[1] * x * y + equation[2] * y^2 + equation[3] * x + equation[4] * y + equation[5] = 0</documentation></function></asyxml>*/ + if(equation.length != 6) abort("intersectionpoints: bad length of array for a conic equation."); + return intersectionpoints(A, B, equation[0], equation[1], equation[2], + equation[3], equation[4], equation[5]); +} +// *........................HEADER.........................* +// *=======================================================* + +// *=======================================================* +// *......................COORDINATES......................* + +real EPS = sqrt(realEpsilon); + +/*<asyxml><typedef type = "convert" return = "pair" params = "pair"><code></asyxml>*/ +typedef pair convert(pair);/*<asyxml></code><documentation>Function type to convert pair in an other coordinate system.</documentation></typedef></asyxml>*/ +/*<asyxml><typedef type = "abs" return = "real" params = "pair"><code></asyxml>*/ +typedef real abs(pair);/*<asyxml></code><documentation>Function type to calculate modulus of pair.</documentation></typedef></asyxml>*/ +/*<asyxml><typedef type = "dot" return = "real" params = "pair, pair"><code></asyxml>*/ +typedef real dot(pair, pair);/*<asyxml></code><documentation>Function type to calculate dot product.</documentation></typedef></asyxml>*/ +/*<asyxml><typedef type = "polar" return = "pair" params = "real, real"><code></asyxml>*/ +typedef pair polar(real, real);/*<asyxml></code><documentation>Function type to calculate the coordinates from the polar coordinates.</documentation></typedef></asyxml>*/ + +/*<asyxml><struct signature="coordsys"><code></asyxml>*/ +struct coordsys +{/*<asyxml></code><documentation>This structure represents a coordinate system in the plane.</documentation></asyxml>*/ + /*<asyxml><method type = "pair" signature="relativetodefault(pair)"><code></asyxml>*/ + restricted convert relativetodefault = new pair(pair m){return m;};/*<asyxml></code><documentation>Convert a pair given relatively to this coordinate system to + the pair relatively to the default coordinate system.</documentation></method></asyxml>*/ + /*<asyxml><method type = "pair" signature="defaulttorelativet(pair)"><code></asyxml>*/ + restricted convert defaulttorelative = new pair(pair m){return m;};/*<asyxml></code><documentation>Convert a pair given relatively to the default coordinate system to + the pair relatively to this coordinate system.</documentation></method></asyxml>*/ + /*<asyxml><method type = "real" signature="dot(pair,pair)"><code></asyxml>*/ + restricted dot dot = new real(pair m, pair n){return dot(m, n);};/*<asyxml></code><documentation>Return the dot product of this coordinate system.</documentation></method></asyxml>*/ + /*<asyxml><method type = "real" signature="abs(pair)"><code></asyxml>*/ + restricted abs abs = new real(pair m){return abs(m);};/*<asyxml></code><documentation>Return the modulus of a pair in this coordinate system.</documentation></method></asyxml>*/ + /*<asyxml><method type = "pair" signature="polar(real,real)"><code></asyxml>*/ + restricted polar polar = new pair(real r, real a){return (r * cos(a), r * sin(a));};/*<asyxml></code><documentation>Polar coordinates routine of this coordinate system.</documentation></method></asyxml>*/ + /*<asyxml><property type = "pair" signature="O,i,j"><code></asyxml>*/ + restricted pair O = (0, 0), i = (1, 0), j = (0, 1);/*<asyxml></code><documentation>Origin and units vector.</documentation></property></asyxml>*/ + /*<asyxml><method type = "void" signature="init(convert,convert,polar,dot)"><code></asyxml>*/ + void init(convert rtd, convert dtr, + polar polar, dot dot) + {/*<asyxml></code><documentation>The default constructor of the coordinate system.</documentation></method></asyxml>*/ + this.relativetodefault = rtd; + this.defaulttorelative = dtr; + this.polar = polar; + this.dot = dot; + this.abs = new real(pair m){return sqrt(dot(m, m));};; + this.O = rtd((0, 0)); + this.i = rtd((1, 0)) - O; + this.j = rtd((0, 1)) - O; + } +}/*<asyxml></struct></asyxml>*/ + +/*<asyxml><operator type = "bool" signature="==(coordsys,coordsys)"><code></asyxml>*/ +bool operator ==(coordsys c1, coordsys c2) +{/*<asyxml></code><documentation>Return true iff the coordinate system have the same origin and units vector.</documentation></operator></asyxml>*/ + return c1.O == c2.O && c1.i == c2.i && c1.j == c2.j; +} + +/*<asyxml><function type="coordsys" signature="cartesiansystem(pair,pair,pair)"><code></asyxml>*/ +coordsys cartesiansystem(pair O = (0, 0), pair i, pair j) +{/*<asyxml></code><documentation>Return the Cartesian coordinate system (O, i, j).</documentation></function></asyxml>*/ + coordsys R; + real[][] P = {{0, 0}, {0, 0}}; + real[][] iP; + P[0][0] = i.x; + P[0][1] = j.x; + P[1][0] = i.y; + P[1][1] = j.y; + iP = inverse(P); + real ni = abs(i); + real nj = abs(j); + real ij = angle(j) - angle(i); + + pair rtd(pair m) + { + return O + (P[0][0] * m.x + P[0][1] * m.y, P[1][0] * m.x + P[1][1] * m.y); + } + + pair dtr(pair m) + { + m-=O; + return (iP[0][0] * m.x + iP[0][1] * m.y, iP[1][0] * m.x + iP[1][1] * m.y); + } + + pair polar(real r, real a) + { + real ca = sin(ij - a)/(ni * sin(ij)); + real sa = sin(a)/(nj * sin(ij)); + return r * (ca, sa); + } + + real tdot(pair m, pair n) + { + return m.x * n.x * ni^2 + m.y * n.y * nj^2 + (m.x * n.y + n.x * m.y) * dot(i, j); + } + + R.init(rtd, dtr, polar, tdot); + return R; +} + + +/*<asyxml><function type="void" signature="show(picture,Label,Label,Label,coordsys,pen,pen,pen,pen,pen)"><code></asyxml>*/ +void show(picture pic = currentpicture, Label lo = "$O$", + Label li = "$\vec{\imath}$", + Label lj = "$\vec{\jmath}$", + coordsys R, + pen dotpen = currentpen, pen xpen = currentpen, pen ypen = xpen, + pen ipen = red, + pen jpen = ipen, + arrowbar arrow = Arrow) +{/*<asyxml></code><documentation>Draw the components (O, i, j, x - axis, y - axis) of 'R'.</documentation></function></asyxml>*/ + unravel R; + dot(pic, O, dotpen); + drawline(pic, O, O + i, xpen); + drawline(pic, O, O + j, ypen); + draw(pic, li, O--(O + i), ipen, arrow); + Label lj = lj.copy(); + lj.align(lj.align, unit(I * j)); + draw(pic, lj, O--(O + j), jpen, arrow); + draw(pic, lj, O--(O + j), jpen, arrow); + Label lo = lo.copy(); + lo.align(lo.align, -2 * dir(O--O + i, O--O + j)); + lo.p(dotpen); + label(pic, lo, O); +} + +/*<asyxml><operator type = "pair" signature="/(pair,coordsys)"><code></asyxml>*/ +pair operator /(pair p, coordsys R) +{/*<asyxml></code><documentation>Return the xy - coordinates of 'p' relatively to + the coordinate system 'R'. + For example, if R = cartesiansystem((1, 2), (1, 0), (0, 1)), (0, 0)/R is (-1, -2).</documentation></operator></asyxml>*/ + return R.defaulttorelative(p); +} + +/*<asyxml><operator type = "pair" signature="*(coordsys,pair)"><code></asyxml>*/ +pair operator *(coordsys R, pair p) +{/*<asyxml></code><documentation>Return the coordinates of 'p' given in the + xy - coordinates 'R'. + For example, if R = cartesiansystem((1, 2), (1, 0), (0, 1)), R * (0, 0) is (1, 2).</documentation></operator></asyxml>*/ + return R.relativetodefault(p); +} + +/*<asyxml><operator type = "path" signature="*(coordsys,path)"><code></asyxml>*/ +path operator *(coordsys R, path g) +{/*<asyxml></code><documentation>Return the reconstructed path applying R * pair to each node, pre and post control point of 'g'.</documentation></operator></asyxml>*/ + guide og = R * point(g, 0); + real l = length(g); + for(int i = 1; i <= l; ++i) + { + pair P = R * point(g, i); + pair post = R * postcontrol(g, i - 1); + pair pre = R * precontrol(g, i); + if(i == l && (cyclic(g))) + og = og..controls post and pre..cycle; + else + og = og..controls post and pre..P; + } + return og; +} + +/*<asyxml><operator type = "coordsys" signature="*(transform,coordsys)"><code></asyxml>*/ +coordsys operator *(transform t,coordsys R) +{/*<asyxml></code><documentation>Provide transform * coordsys. + Note that shiftless(t) is applied to R.i and R.j.</documentation></operator></asyxml>*/ + coordsys oc; + oc = cartesiansystem(t * R.O, shiftless(t) * R.i, shiftless(t) * R.j); + return oc; +} + +/*<asyxml><constant type = "coordsys" signature="defaultcoordsys"><code></asyxml>*/ +restricted coordsys defaultcoordsys = cartesiansystem(0, (1, 0), (0, 1));/*<asyxml></code><documentation>One can always refer to the default coordinate system using this constant.</documentation></constant></asyxml>*/ +/*<asyxml><variable type="coordsys" signature="currentcoordsys"><code></asyxml>*/ +coordsys currentcoordsys = defaultcoordsys;/*<asyxml></code><documentation>The coordinate system used by default.</documentation></variable></asyxml>*/ + +/*<asyxml><struct signature="point"><code></asyxml>*/ +struct point +{/*<asyxml></code><documentation>This structure replaces the pair to embed its coordinate system. + For example, if 'P = point(cartesiansystem((1, 2), i, j), (0, 0))', + P is equal to the pair (1, 2).</documentation></asyxml>*/ + /*<asyxml><property type = "coordsys" signature="coordsys"><code></asyxml>*/ + coordsys coordsys;/*<asyxml></code><documentation>The coordinate system of this point.</documentation></property><property type = "pair" signature="coordinates"><code></asyxml>*/ + restricted pair coordinates;/*<asyxml></code><documentation>The coordinates of this point relatively to the coordinate system 'coordsys'.</documentation></property><property type = "real" signature="x, y"><code></asyxml>*/ + restricted real x, y;/*<asyxml></code><documentation>The xpart and the ypart of 'coordinates'.</documentation></property></asyxml>*/ + /*<asyxml><method type = "" signature="init(coordsys,pair)"><code><property type = "real" signature="m"><code></asyxml>*/ + real m = 1;/*<asyxml></code><documentation>Used to cast mass<->point.</documentation></property></asyxml>*/ + void init(coordsys R, pair coordinates, real mass) + {/*<asyxml></code><documentation>The constructor.</documentation></method></asyxml>*/ + this.coordsys = R; + this.coordinates = coordinates; + this.x = coordinates.x; + this.y = coordinates.y; + this.m = mass; + } +}/*<asyxml></struct></asyxml>*/ + +/*<asyxml><function type="point" signature="point(coordsys,pair,real)"><code></asyxml>*/ +point point(coordsys R, pair p, real m = 1) +{/*<asyxml></code><documentation>Return the point which has the coodinates 'p' in the + coordinate system 'R' and the mass 'm'.</documentation></function></asyxml>*/ + point op; + op.init(R, p, m); + return op; +} + +/*<asyxml><function type="point" signature="point(explicit pair,real)"><code></asyxml>*/ +point point(explicit pair p, real m) +{/*<asyxml></code><documentation>Return the point which has the coodinates 'p' in the current + coordinate system and the mass 'm'.</documentation></function></asyxml>*/ + point op; + op.init(currentcoordsys, p, m); + return op; +} + +/*<asyxml><function type="point" signature="point(coordsys,explicit point,real)"><code></asyxml>*/ +point point(coordsys R, explicit point M, real m = M.m) +{/*<asyxml></code><documentation>Return the point of 'R' which has the coordinates of 'M' and the mass 'm'. + Do not confuse this routine with the further routine 'changecoordsys'.</documentation></function></asyxml>*/ + point op; + op.init(R, M.coordinates, M.m); + return op; +} + +/*<asyxml><function type="point" signature="changecoordsys(coordsys,point)"><code></asyxml>*/ +point changecoordsys(coordsys R, point M) +{/*<asyxml></code><documentation>Return the point 'M' in the coordinate system 'coordsys'. + In other words, the returned point marks the same plot as 'M' does.</documentation></function></asyxml>*/ + point op; + coordsys mco = M.coordsys; + op.init(R, R.defaulttorelative(mco.relativetodefault(M.coordinates)), M.m); + return op; +} + +/*<asyxml><function type="pair" signature="pair coordinates(point)"><code></asyxml>*/ +pair coordinates(point M) +{/*<asyxml></code><documentation>Return the coordinates of 'M' in its coordinate system.</documentation></function></asyxml>*/ + return M.coordinates; +} + +/*<asyxml><function type="bool" signature="bool samecoordsys(bool...point[])"><code></asyxml>*/ +bool samecoordsys(bool warn = true ... point[] M) +{/*<asyxml></code><documentation>Return true iff all the points have the same coordinate system. + If 'warn' is true and the coordinate systems are different, a warning is sent.</documentation></function></asyxml>*/ + bool ret = true; + coordsys t = M[0].coordsys; + for (int i = 1; i < M.length; ++i) { + ret = (t == M[i].coordsys); + if(!ret) break; + t = M[i].coordsys; + } + if(warn && !ret) + warning("coodinatesystem", + "the coordinate system of two objects are not the same. +The operation will be done relative to the default coordinate system."); + return ret; +} + +/*<asyxml><function type="point[]" signature="standardizecoordsys(coordsys,bool...point[])"><code></asyxml>*/ +point[] standardizecoordsys(coordsys R = currentcoordsys, + bool warn = true ... point[] M) +{/*<asyxml></code><documentation>Return the points with the same coordinate system 'R'. + If 'warn' is true and the coordinate systems are different, a warning is sent.</documentation></function></asyxml>*/ + point[] op = new point[]; + op = M; + if(!samecoordsys(warn ... M)) + for (int i = 1; i < M.length; ++i) + op[i] = changecoordsys(R, M[i]); + return op; +} + +/*<asyxml><operator type = "pair" signature="cast(point)"><code></asyxml>*/ +pair operator cast(point P) +{/*<asyxml></code><documentation>Cast point to pair.</documentation></operator></asyxml>*/ + return P.coordsys.relativetodefault(P.coordinates); +} + +/*<asyxml><operator type = "pair[]" signature="cast(point[])"><code></asyxml>*/ +pair[] operator cast(point[] P) +{/*<asyxml></code><documentation>Cast point[] to pair[].</documentation></operator></asyxml>*/ + pair[] op; + for (int i = 0; i < P.length; ++i) { + op.push((pair)P[i]); + } + return op; +} + +/*<asyxml><operator type = "point" signature="cast(pair)"><code></asyxml>*/ +point operator cast(pair p) +{/*<asyxml></code><documentation>Cast pair to point relatively to the current coordinate + system 'currentcoordsys'.</documentation></operator></asyxml>*/ + return point(currentcoordsys, p); +} + +/*<asyxml><operator type = "point[]" signature="cast(pair[])"><code></asyxml>*/ +point[] operator cast(pair[] p) +{/*<asyxml></code><documentation>Cast pair[] to point[] relatively to the current coordinate + system 'currentcoordsys'.</documentation></operator></asyxml>*/ + pair[] op; + for (int i = 0; i < p.length; ++i) { + op.push((point)p[i]); + } + return op; +} + +/*<asyxml><function type="pair" signature="locate(point)"><code></asyxml>*/ +pair locate(point P) +{/*<asyxml></code><documentation>Return the coordinates of 'P' in the default coordinate system.</documentation></function></asyxml>*/ + return P.coordsys * P.coordinates; +} + +/*<asyxml><function type="point" signature="locate(pair)"><code></asyxml>*/ +point locate(pair p) +{/*<asyxml></code><documentation>Return the point in the current coordinate system 'currentcoordsys'.</documentation></function></asyxml>*/ + return p; //automatic casting 'pair to point'. +} + +/*<asyxml><operator type = "point" signature="*(real,explicit point)"><code></asyxml>*/ +point operator *(real x, explicit point P) +{/*<asyxml></code><documentation>Multiply the coordinates (not the mass) of 'P' by 'x'.</documentation></operator></asyxml>*/ + return point(P.coordsys, x * P.coordinates, P.m); +} + +/*<asyxml><operator type = "point" signature="/(explicit point,real)"><code></asyxml>*/ +point operator /(explicit point P, real x) +{/*<asyxml></code><documentation>Divide the coordinates (not the mass) of 'P' by 'x'.</documentation></operator></asyxml>*/ + return point(P.coordsys, P.coordinates/x, P.m); +} + +/*<asyxml><operator type = "point" signature="/(real,explicit point)"><code></asyxml>*/ +point operator /(real x, explicit point P) +{/*<asyxml></code><documentation></documentation></operator></asyxml>*/ + return point(P.coordsys, x/P.coordinates, P.m); +} + +/*<asyxml><operator type = "point" signature="-(explicit point)"><code></asyxml>*/ +point operator -(explicit point P) +{/*<asyxml></code><documentation>-P. The mass is inchanged.</documentation></operator></asyxml>*/ + return point(P.coordsys, -P.coordinates, P.m); +} + +/*<asyxml><operator type = "point" signature="+(explicit point,explicit point)"><code></asyxml>*/ +point operator +(explicit point P1, explicit point P2) +{/*<asyxml></code><documentation>Provide 'point + point'. + If the two points haven't the same coordinate system, a warning is sent and the + returned point has the default coordinate system 'defaultcoordsys'. + The masses are added.</documentation></operator></asyxml>*/ + point[] P = standardizecoordsys(P1, P2); + coordsys R = P[0].coordsys; + return point(R, P[0].coordinates + P[1].coordinates, P1.m + P2.m); +} + +/*<asyxml><operator type = "point" signature="+(explicit point,explicit pair)"><code></asyxml>*/ +point operator +(explicit point P1, explicit pair p2) +{/*<asyxml></code><documentation>Provide 'point + pair'. + The pair 'p2' is supposed to be coordinates relatively to the coordinates system of 'P1'. + The mass is not changed.</documentation></operator></asyxml>*/ + coordsys R = currentcoordsys; + return point(R, P1.coordinates + point(R, p2).coordinates, P1.m); +} +point operator +(explicit pair p1, explicit point p2) +{ + return p2 + p1; +} + +/*<asyxml><operator type = "point" signature="-(explicit point,explicit point)"><code></asyxml>*/ +point operator -(explicit point P1, explicit point P2) +{/*<asyxml></code><documentation>Provide 'point - point'.</documentation></operator></asyxml>*/ + return P1 + (-P2); +} + +/*<asyxml><operator type = "point" signature="-(explicit point,explicit pair)"><code></asyxml>*/ +point operator -(explicit point P1, explicit pair p2) +{/*<asyxml></code><documentation>Provide 'point - pair'. + The pair 'p2' is supposed to be coordinates relatively to the coordinates system of 'P1'.</documentation></operator></asyxml>*/ + return P1 + (-p2); +} +point operator -(explicit pair p1, explicit point P2) +{ + return p1 + (-P2); +} + +/*<asyxml><operator type = "point" signature="*(transform,explicit point)"><code></asyxml>*/ +point operator *(transform t, explicit point P) +{/*<asyxml></code><documentation>Provide 'transform * point'. + Note that the transforms scale, xscale, yscale and rotate are carried out relatively + the default coordinate system 'defaultcoordsys' which is not desired for point + defined in an other coordinate system. + On can use scale(real, point), xscale(real, point), yscale(real, point), rotate(real, point), + scaleO(real), xscaleO(real), yscaleO(real) and rotateO(real) (described further) + to change the coordinate system of reference.</documentation></operator></asyxml>*/ + coordsys R = P.coordsys; + return point(R, (t * locate(P))/R, P.m); +} + +/*<asyxml><operator type = "point" signature="*(explicit point,explicit point)"><code></asyxml>*/ +point operator *(explicit point P1, explicit point P2) +{/*<asyxml></code><documentation>Provide 'point * point'. + The resulted mass is the mass of P2</documentation></operator></asyxml>*/ + point[] P = standardizecoordsys(P1, P2); + coordsys R = P[0].coordsys; + return point(R, P[0].coordinates * P[1].coordinates, P2.m); +} + +/*<asyxml><operator type = "point" signature="*(explicit point,explicit pair)"><code></asyxml>*/ +point operator *(explicit point P1, explicit pair p2) +{/*<asyxml></code><documentation>Provide 'point * pair'. + The pair 'p2' is supposed to be the coordinates of + the point in the coordinates system of 'P1'. + 'pair * point' is also defined.</documentation></operator></asyxml>*/ + point P = point(P1.coordsys, p2, P1.m); + return P1 * P; +} +point operator *(explicit pair p1, explicit point p2) +{ + return p2 * p1; +} + +/*<asyxml><operator type = "bool" signature="==(explicit point,explicit point)"><code></asyxml>*/ +bool operator ==(explicit point M, explicit point N) +{/*<asyxml></code><documentation>Provide the test 'M == N' wish returns true iff MN < EPS</documentation></operator></asyxml>*/ + return abs(locate(M) - locate(N)) < EPS; +} + +/*<asyxml><operator type = "bool" signature="!=(explicit point,explicit point)"><code></asyxml>*/ +bool operator !=(explicit point M, explicit point N) +{/*<asyxml></code><documentation>Provide the test 'M != N' wish return true iff MN >= EPS</documentation></operator></asyxml>*/ + return !(M == N); +} + +/*<asyxml><operator type = "guide" signature="cast(point)"><code></asyxml>*/ +guide operator cast(point p) +{/*<asyxml></code><documentation>Cast point to guide.</documentation></operator></asyxml>*/ + return locate(p); +} + +/*<asyxml><operator type = "path" signature="cast(point)"><code></asyxml>*/ +path operator cast(point p) +{/*<asyxml></code><documentation>Cast point to path.</documentation></operator></asyxml>*/ + return locate(p); +} + +/*<asyxml><function type="void" signature="dot(picture,Label,explicit point,align,string,pen)"><code></asyxml>*/ +void dot(picture pic = currentpicture, Label L, explicit point Z, + align align = NoAlign, + string format = defaultformat, pen p = currentpen) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + Label L = L.copy(); + L.position(locate(Z)); + if(L.s == "") { + if(format == "") format = defaultformat; + L.s = "("+format(format, Z.x)+", "+format(format, Z.y)+")"; + } + L.align(align, E); + L.p(p); + dot(pic, locate(Z), p); + add(pic, L); +} + +/*<asyxml><function type="real" signature="abs(coordsys,pair)"><code></asyxml>*/ +real abs(coordsys R, pair m) +{/*<asyxml></code><documentation>Return the modulus |m| in the coordinate system 'R'.</documentation></function></asyxml>*/ + return R.abs(m); +} + +/*<asyxml><function type="real" signature="abs(explicit point)"><code></asyxml>*/ +real abs(explicit point M) +{/*<asyxml></code><documentation>Return the modulus |M| in its coordinate system.</documentation></function></asyxml>*/ + return M.coordsys.abs(M.coordinates); +} + +/*<asyxml><function type="real" signature="length(explicit point)"><code></asyxml>*/ +real length(explicit point M) +{/*<asyxml></code><documentation>Return the modulus |M| in its coordinate system (same as 'abs').</documentation></function></asyxml>*/ + return M.coordsys.abs(M.coordinates); +} + +/*<asyxml><function type="point" signature="conj(explicit point)"><code></asyxml>*/ +point conj(explicit point M) +{/*<asyxml></code><documentation>Conjugate.</documentation></function></asyxml>*/ + return point(M.coordsys, conj(M.coordinates), M.m); +} + +/*<asyxml><function type="real" signature="degrees(explicit point,coordsys,bool)"><code></asyxml>*/ +real degrees(explicit point M, coordsys R = M.coordsys, bool warn = true) +{/*<asyxml></code><documentation>Return the angle of M (in degrees) relatively to 'R'.</documentation></function></asyxml>*/ + return (degrees(locate(M) - R.O, warn) - degrees(R.i))%360; +} + +/*<asyxml><function type="real" signature="angle(explicit point,coordsys,bool)"><code></asyxml>*/ +real angle(explicit point M, coordsys R = M.coordsys, bool warn = true) +{/*<asyxml></code><documentation>Return the angle of M (in radians) relatively to 'R'.</documentation></function></asyxml>*/ + return radians(degrees(M, R, warn)); +} + +/*<asyxml><function type="bool" signature="finite(explicit point)"><code></asyxml>*/ +bool finite(explicit point p) +{/*<asyxml></code><documentation>Avoid to compute 'finite((pair)(infinite_point))'.</documentation></function></asyxml>*/ + return finite(p.coordinates); +} + +/*<asyxml><function type="real" signature="dot(point,point)"><code></asyxml>*/ +real dot(point A, point B) +{/*<asyxml></code><documentation>Return the dot product in the coordinate system of 'A'.</documentation></function></asyxml>*/ + point[] P = standardizecoordsys(A.coordsys, A, B); + return P[0].coordsys.dot(P[0].coordinates, P[1].coordinates); +} + +/*<asyxml><function type="real" signature="dot(point,explicit pair)"><code></asyxml>*/ +real dot(point A, explicit pair B) +{/*<asyxml></code><documentation>Return the dot product in the default coordinate system. + dot(explicit pair, point) is also defined.</documentation></function></asyxml>*/ + return dot(locate(A), B); +} +real dot(explicit pair A, point B) +{ + return dot(A, locate(B)); +} + +/*<asyxml><function type="transforms" signature="rotateO(real)"><code></asyxml>*/ +transform rotateO(real a) +{/*<asyxml></code><documentation>Rotation around the origin of the current coordinate system.</documentation></function></asyxml>*/ + return rotate(a, currentcoordsys.O); +}; + +/*<asyxml><function type="transform" signature="projection(point,point)"><code></asyxml>*/ +transform projection(point A, point B) +{/*<asyxml></code><documentation>Return the orthogonal projection on the line (AB).</documentation></function></asyxml>*/ + pair dir = unit(locate(A) - locate(B)); + pair a = locate(A); + real cof = dir.x * a.x + dir.y * a.y; + real tx = a.x - dir.x * cof; + real txx = dir.x^2; + real txy = dir.x * dir.y; + real ty = a.y - dir.y * cof; + real tyx = txy; + real tyy = dir.y^2; + transform t = (tx, ty, txx, txy, tyx, tyy); + return t; +} + +/*<asyxml><function type="transform" signature="projection(point,point,point,point,bool)"><code></asyxml>*/ +transform projection(point A, point B, point C, point D, bool safe = false) +{/*<asyxml></code><documentation>Return the (CD) parallel projection on (AB). + If 'safe = true' and (AB)//(CD) return the identity. + If 'safe = false' and (AB)//(CD) return an infinity scaling.</documentation></function></asyxml>*/ + pair a = locate(A); + pair u = unit(locate(B) - locate(A)); + pair v = unit(locate(D) - locate(C)); + real c = u.x * a.y - u.y * a.x; + real d = (conj(u) * v).y; + if (abs(d) < epsgeo) { + return safe ? identity() : scale(infinity); + } + real tx = c * v.x/d; + real ty = c * v.y/d; + real txx = u.x * v.y/d; + real txy = -u.x * v.x/d; + real tyx = u.y * v.y/d; + real tyy = -u.y * v.x/d; + transform t = (tx, ty, txx, txy, tyx, tyy); + return t; +} + +/*<asyxml><function type="transform" signature="scale(real,point)"><code></asyxml>*/ +transform scale(real k, point M) +{/*<asyxml></code><documentation>Homothety.</documentation></function></asyxml>*/ + pair P = locate(M); + return shift(P) * scale(k) * shift(-P); +} + +/*<asyxml><function type="transform" signature="xscale(real,point)"><code></asyxml>*/ +transform xscale(real k, point M) +{/*<asyxml></code><documentation>xscale from 'M' relatively to the x - axis of the coordinate system of 'M'.</documentation></function></asyxml>*/ + pair P = locate(M); + real a = degrees(M.coordsys.i); + return (shift(P) * rotate(a)) * xscale(k) * (rotate(-a) * shift(-P)); +} + +/*<asyxml><function type="transform" signature="yscale(real,point)"><code></asyxml>*/ +transform yscale(real k, point M) +{/*<asyxml></code><documentation>yscale from 'M' relatively to the y - axis of the coordinate system of 'M'.</documentation></function></asyxml>*/ + pair P = locate(M); + real a = degrees(M.coordsys.j) - 90; + return (shift(P) * rotate(a)) * yscale(k) * (rotate(-a) * shift(-P)); +} + +/*<asyxml><function type="transform" signature="scale(real,point,point,point,point,bool)"><code></asyxml>*/ +transform scale(real k, point A, point B, point C, point D, bool safe = false) +{/*<asyxml></code><documentation><url href = "http://fr.wikipedia.org/wiki/Affinit%C3%A9_%28math%C3%A9matiques%29"/> + (help me for English translation...) + If 'safe = true' and (AB)//(CD) return the identity. + If 'safe = false' and (AB)//(CD) return a infinity scaling.</documentation></function></asyxml>*/ + pair a = locate(A); + pair u = unit(locate(B) - locate(A)); + pair v = unit(locate(D) - locate(C)); + real c = u.x * a.y - u.y * a.x; + real d = (conj(u) * v).y; + real d = (conj(u) * v).y; + if (abs(d) < epsgeo) { + return safe ? identity() : scale(infinity); + } + real tx = (1 - k) * c * v.x/d; + real ty = (1 - k) * c * v.y/d; + real txx = (1 - k) * u.x * v.y/d + k; + real txy = (k - 1) * u.x * v.x/d; + real tyx = (1 - k) * u.y * v.y/d; + real tyy = (k - 1) * u.y * v.x/d + k; + transform t = (tx, ty, txx, txy, tyx, tyy); + return t; +} + +/*<asyxml><function type="transform" signature="scaleO(real)"><code></asyxml>*/ +transform scaleO(real x) +{/*<asyxml></code><documentation>Homothety from the origin of the current coordinate system.</documentation></function></asyxml>*/ + return scale(x, (0, 0)); +} + +/*<asyxml><function type="transform" signature="xscaleO(real)"><code></asyxml>*/ +transform xscaleO(real x) +{/*<asyxml></code><documentation>xscale from the origin and relatively to the current coordinate system.</documentation></function></asyxml>*/ + return scale(x, (0, 0), (0, 1), (0, 0), (1, 0)); +} + +/*<asyxml><function type="transform" signature="yscaleO(real)"><code></asyxml>*/ +transform yscaleO(real x) +{/*<asyxml></code><documentation>yscale from the origin and relatively to the current coordinate system.</documentation></function></asyxml>*/ + return scale(x, (0, 0), (1, 0), (0, 0), (0, 1)); +} + +/*<asyxml><struct signature="vector"><code></asyxml>*/ +struct vector +{/*<asyxml></code><documentation>Like a point but casting to pair, adding etc does not take account + of the origin of the coordinate system.</documentation><property type = "point" signature="v"><code></asyxml>*/ + point v;/*<asyxml></code><documentation>Coordinates as a point (embed coordinate system and pair).</documentation></property></asyxml>*/ +}/*<asyxml></struct></asyxml>*/ + +/*<asyxml><operator type = "point" signature="cast(vector)"><code></asyxml>*/ +point operator cast(vector v) +{/*<asyxml></code><documentation>Cast vector 'v' to point 'M' so that OM = v.</documentation></operator></asyxml>*/ + return v.v; +} + +/*<asyxml><operator type = "vector" signature="cast(pair)"><code></asyxml>*/ +vector operator cast(pair v) +{/*<asyxml></code><documentation>Cast pair to vector relatively to the current coordinate + system 'currentcoordsys'.</documentation></operator></asyxml>*/ + vector ov; + ov.v = point(currentcoordsys, v); + return ov; +} + +/*<asyxml><operator type = "vector" signature="cast(explicit point)"><code></asyxml>*/ +vector operator cast(explicit point v) +{/*<asyxml></code><documentation>A point can be interpreted like a vector using the code + '(vector)a_point'.</documentation></operator></asyxml>*/ + vector ov; + ov.v = v; + return ov; +} + +/*<asyxml><operator type = "pair" signature="cast(explicit vector)"><code></asyxml>*/ +pair operator cast(explicit vector v) +{/*<asyxml></code><documentation>Cast vector to pair (the coordinates of 'v' in the default coordinate system).</documentation></operator></asyxml>*/ + return locate(v.v) - v.v.coordsys.O; +} + +/*<asyxml><operator type = "align" signature="cast(vector)"><code></asyxml>*/ +align operator cast(vector v) +{/*<asyxml></code><documentation>Cast vector to align.</documentation></operator></asyxml>*/ + return (pair)v; +} + +/*<asyxml><function type="vector" signature="vector(coordsys, pair)"><code></asyxml>*/ +vector vector(coordsys R = currentcoordsys, pair v) +{/*<asyxml></code><documentation>Return the vector of 'R' which has the coordinates 'v'.</documentation></function></asyxml>*/ + vector ov; + ov.v = point(R, v); + return ov; +} + +/*<asyxml><function type="vector" signature="vector(point)"><code></asyxml>*/ +vector vector(point M) +{/*<asyxml></code><documentation>Return the vector OM, where O is the origin of the coordinate system of 'M'. + Useful to write 'vector(P - M);' instead of '(vector)(P - M)'.</documentation></function></asyxml>*/ + return M; +} + +/*<asyxml><function type="point" signature="point(explicit vector)"><code></asyxml>*/ +point point(explicit vector u) +{/*<asyxml></code><documentation>Return the point M so that OM = u, where O is the origin of the coordinate system of 'u'.</documentation></function></asyxml>*/ + return u.v; +} + +/*<asyxml><function type="pair" signature="locate(explicit vector)"><code></asyxml>*/ +pair locate(explicit vector v) +{/*<asyxml></code><documentation>Return the coordinates of 'v' in the default coordinate system (like casting vector to pair).</documentation></function></asyxml>*/ + return (pair)v; +} + +/*<asyxml><function type="void" signature="show(Label,pen,arrowbar)"><code></asyxml>*/ +void show(Label L, vector v, pen p = currentpen, arrowbar arrow = Arrow) +{/*<asyxml></code><documentation>Draw the vector v (from the origin of its coordinate system).</documentation></function></asyxml>*/ + coordsys R = v.v.coordsys; + draw(L, R.O--v.v, p, arrow); +} + +/*<asyxml><function type="vector" signature="changecoordsys(coordsys,vector)"><code></asyxml>*/ +vector changecoordsys(coordsys R, vector v) +{/*<asyxml></code><documentation>Return the vector 'v' relatively to coordinate system 'R'.</documentation></function></asyxml>*/ + vector ov; + ov.v = point(R, (locate(v) + R.O)/R); + return ov; +} + +/*<asyxml><operator type = "vector" signature="*(real,explicit vector)"><code></asyxml>*/ +vector operator *(real x, explicit vector v) +{/*<asyxml></code><documentation>Provide real * vector.</documentation></operator></asyxml>*/ + return x * v.v; +} + +/*<asyxml><operator type = "vector" signature="/(explicit vector,real)"><code></asyxml>*/ +vector operator /(explicit vector v, real x) +{/*<asyxml></code><documentation>Provide vector/real</documentation></operator></asyxml>*/ + return v.v/x; +} + +/*<asyxml><operator type = "vector" signature="*(transform t,explicit vector)"><code></asyxml>*/ +vector operator *(transform t, explicit vector v) +{/*<asyxml></code><documentation>Provide transform * vector.</documentation></operator></asyxml>*/ + return t * v.v; +} + +/*<asyxml><operator type = "vector" signature="*(explicit point,explicit vector)"><code></asyxml>*/ +vector operator *(explicit point M, explicit vector v) +{/*<asyxml></code><documentation>Provide point * vector</documentation></operator></asyxml>*/ + return M * v.v; +} + +/*<asyxml><operator type = "point" signature="+(explicit point,explicit vector)"><code></asyxml>*/ +point operator +(point M, explicit vector v) +{/*<asyxml></code><documentation>Return 'M' shifted by 'v'.</documentation></operator></asyxml>*/ + return shift(locate(v)) * M; +} + +/*<asyxml><operator type = "point" signature="-(explicit point,explicit vector)"><code></asyxml>*/ +point operator -(point M, explicit vector v) +{/*<asyxml></code><documentation>Return 'M' shifted by '-v'.</documentation></operator></asyxml>*/ + return shift(-locate(v)) * M; +} + +/*<asyxml><operator type = "vector" signature="-(explicit vector)"><code></asyxml>*/ +vector operator -(explicit vector v) +{/*<asyxml></code><documentation>Provide -v.</documentation></operator></asyxml>*/ + return -v.v; +} + +/*<asyxml><operator type = "point" signature="+(explicit pair,explicit vector)"><code></asyxml>*/ +point operator +(explicit pair m, explicit vector v) +{/*<asyxml></code><documentation>The pair 'm' is supposed to be the coordinates of + a point in the current coordinates system 'currentcoordsys'. + Return this point shifted by the vector 'v'.</documentation></operator></asyxml>*/ + return locate(m) + v; +} + +/*<asyxml><operator type = "point" signature="-(explicit pair,explicit vector)"><code></asyxml>*/ +point operator -(explicit pair m, explicit vector v) +{/*<asyxml></code><documentation>The pair 'm' is supposed to be the coordinates of + a point in the current coordinates system 'currentcoordsys'. + Return this point shifted by the vector '-v'.</documentation></operator></asyxml>*/ + return m + (-v); +} + +/*<asyxml><operator type = "vector" signature="+(explicit vector,explicit vector)"><code></asyxml>*/ +vector operator +(explicit vector v1, explicit vector v2) +{/*<asyxml></code><documentation>Provide vector + vector. + If the two vector haven't the same coordinate system, the returned + vector is relative to the default coordinate system (without warning).</documentation></operator></asyxml>*/ + coordsys R = v1.v.coordsys; + if(samecoordsys(false, v1, v2)){R = defaultcoordsys;} + return vector(R, (locate(v1) + locate(v2))/R); +} + +/*<asyxml><operator type = "vector" signature="-(explicit vector, explicit vector)"><code></asyxml>*/ +vector operator -(explicit vector v1, explicit vector v2) +{/*<asyxml></code><documentation>Provide vector - vector. + If the two vector haven't the same coordinate system, the returned + vector is relative to the default coordinate system (without warning).</documentation></operator></asyxml>*/ + return v1 + (-v2); +} + +/*<asyxml><operator type = "bool" signature="==(explicit vector,explicit vector)"><code></asyxml>*/ +bool operator ==(explicit vector u, explicit vector v) +{/*<asyxml></code><documentation>Return true iff |u - v|<EPS.</documentation></operator></asyxml>*/ + return abs(u - v) < EPS; +} + +/*<asyxml><function type="bool" signature="collinear(vector,vector)"><code></asyxml>*/ +bool collinear(vector u, vector v) +{/*<asyxml></code><documentation>Return 'true' iff the vectors 'u' and 'v' are collinear.</documentation></function></asyxml>*/ + return abs(ypart((conj((pair)u) * (pair)v))) < EPS; +} + +/*<asyxml><function type="vector" signature="unit(point)"><code></asyxml>*/ +vector unit(point M) +{/*<asyxml></code><documentation>Return the unit vector according to the modulus of its coordinate system.</documentation></function></asyxml>*/ + return M/abs(M); +} + +/*<asyxml><function type="vector" signature="unit(vector)"><code></asyxml>*/ +vector unit(vector u) +{/*<asyxml></code><documentation>Return the unit vector according to the modulus of its coordinate system.</documentation></function></asyxml>*/ + return u.v/abs(u.v); +} + +/*<asyxml><function type="real" signature="degrees(vector,coordsys,bool)"><code></asyxml>*/ +real degrees(vector v, + coordsys R = v.v.coordsys, + bool warn = true) +{/*<asyxml></code><documentation>Return the angle of 'v' (in degrees) relatively to 'R'.</documentation></function></asyxml>*/ + return (degrees(locate(v), warn) - degrees(R.i))%360; +} + +/*<asyxml><function type="real" signature="angle(vector,coordsys,bool)"><code></asyxml>*/ +real angle(explicit vector v, + coordsys R = v.v.coordsys, + bool warn = true) +{/*<asyxml></code><documentation>Return the angle of 'v' (in radians) relatively to 'R'.</documentation></function></asyxml>*/ + return radians(degrees(v, R, warn)); +} + +/*<asyxml><function type="vector" signature="conj(explicit vector)"><code></asyxml>*/ +vector conj(explicit vector u) +{/*<asyxml></code><documentation>Conjugate.</documentation></function></asyxml>*/ + return conj(u.v); +} + +/*<asyxml><function type="transform" signature="rotate(explicit vector)"><code></asyxml>*/ +transform rotate(explicit vector dir) +{/*<asyxml></code><documentation>A rotation in the direction 'dir' limited to [-90, 90] + This is useful for rotating text along a line in the direction dir. + rotate(explicit point dir) is also defined. + </documentation></function></asyxml>*/ + return rotate(locate(dir)); +} +transform rotate(explicit point dir){return rotate(locate(vector(dir)));} +// *......................COORDINATES......................* +// *=======================================================* + +// *=======================================================* +// *.........................BASES.........................* +/*<asyxml><variable type="point" signature="origin"><code></asyxml>*/ +point origin = point(defaultcoordsys, (0, 0));/*<asyxml></code><documentation>The origin of the current coordinate system.</documentation></variable></asyxml>*/ + +/*<asyxml><function type="point" signature="origin(coordsys)"><code></asyxml>*/ +point origin(coordsys R = currentcoordsys) +{/*<asyxml></code><documentation>Return the origin of the coordinate system 'R'.</documentation></function></asyxml>*/ + return point(R, (0, 0)); //use automatic casting; +} + +/*<asyxml><variable type="real" signature="linemargin"><code></asyxml>*/ +real linemargin = 0;/*<asyxml></code><documentation>Margin used to draw lines.</documentation></variable></asyxml>*/ +/*<asyxml><function type="real" signature="linemargin()"><code></asyxml>*/ +real linemargin() +{/*<asyxml></code><documentation>Return the margin used to draw lines.</documentation></function></asyxml>*/ + return linemargin; +} + +/*<asyxml><variable type="pen" signature="addpenline"><code></asyxml>*/ +pen addpenline = squarecap;/*<asyxml></code><documentation>Add this property to the drawing pen of "finish" lines.</documentation></variable></asyxml>*/ +pen addpenline(pen p) { + return addpenline + p; +} + +/*<asyxml><variable type="pen" signature="addpenarc"><code></asyxml>*/ +pen addpenarc = squarecap;/*<asyxml></code><documentation>Add this property to the drawing pen of arcs.</documentation></variable></asyxml>*/ +pen addpenarc(pen p) {return addpenarc + p;} + +/*<asyxml><variable type="string" signature="defaultmassformat"><code></asyxml>*/ +string defaultmassformat = "$\left(%L;%.4g\right)$";/*<asyxml></code><documentation>Format used to construct the default label of masses.</documentation></variable></asyxml>*/ + +/*<asyxml><function type="int" signature="sgnd(real)"><code></asyxml>*/ +int sgnd(real x) +{/*<asyxml></code><documentation>Return the -1 if x < 0, 1 if x >= 0.</documentation></function></asyxml>*/ + return (x == 0) ? 1 : sgn(x); +} +int sgnd(int x) +{ + return (x == 0) ? 1 : sgn(x); +} + +/*<asyxml><function type="bool" signature="defined(pair)"><code></asyxml>*/ +bool defined(point P) +{/*<asyxml></code><documentation>Return true iff the coordinates of 'P' are finite.</documentation></function></asyxml>*/ + return finite(P.coordinates); +} + +/*<asyxml><function type="bool" signature="onpath(picture,path,point,pen)"><code></asyxml>*/ +bool onpath(picture pic = currentpicture, path g, point M, pen p = currentpen) +{/*<asyxml></code><documentation>Return true iff 'M' is on the path drawn with the pen 'p' in 'pic'.</documentation></function></asyxml>*/ + transform t = inverse(pic.calculateTransform()); + return intersect(g, shift(locate(M)) * scale(linewidth(p)/2) * t * unitcircle).length > 0; +} + +/*<asyxml><function type="bool" signature="sameside(point,point,point)"><code></asyxml>*/ +bool sameside(point M, point N, point O) +{/*<asyxml></code><documentation>Return 'true' iff 'M' and 'N' are same side of the point 'O'.</documentation></function></asyxml>*/ + pair m = M, n = N, o = O; + return dot(m - o, n - o) >= -epsgeo; +} + +/*<asyxml><function type="bool" signature="between(point,point,point)"><code></asyxml>*/ +bool between(point M, point O, point N) +{/*<asyxml></code><documentation>Return 'true' iff 'O' is between 'M' and 'N'.</documentation></function></asyxml>*/ + return (!sameside(N, M, O) || M == O || N == O); +} + + +typedef path pathModifier(path); +pathModifier NoModifier = new path(path g){return g;}; + +private void Drawline(picture pic = currentpicture, Label L = "", pair P, bool dirP = true, pair Q, bool dirQ = true, + align align = NoAlign, pen p = currentpen, + arrowbar arrow = None, + Label legend = "", marker marker = nomarker, + pathModifier pathModifier = NoModifier) +{/* Add the two parameters 'dirP' and 'dirQ' to the native routine + 'drawline' of the module 'math'. + Segment [PQ] will be prolonged in direction of P if 'dirP = true', in + direction of Q if 'dirQ = true'. + If 'dirP = dirQ = true', the behavior is that of the native 'drawline'. + Add all the other parameters of 'Draw'.*/ + pic.add(new void (frame f, transform t, transform T, pair m, pair M) { + picture opic; + // Reduce the bounds by the size of the pen. + m -= min(p) - (linemargin(), linemargin()); M -= max(p) + (linemargin(), linemargin()); + + // Calculate the points and direction vector in the transformed space. + t = t * T; + pair z = t * P; + pair q = t * Q; + pair v = q - z; + // path g; + pair ptp, ptq; + real cp = dirP ? 1:0; + real cq = dirQ ? 1:0; + // Handle horizontal and vertical lines. + if(v.x == 0) { + if(m.x <= z.x && z.x <= M.x) + if (dot(v, m - z) < 0) { + ptp = (z.x, z.y + cp * (m.y - z.y)); + ptq = (z.x, q.y + cq * (M.y - q.y)); + } else { + ptq = (z.x, q.y + cq * (m.y - q.y)); + ptp = (z.x, z.y + cp * (M.y - z.y)); + } + } else if(v.y == 0) { + if (dot(v, m - z) < 0) { + ptp = (z.x + cp * (m.x - z.x), z.y); + ptq = (q.x + cq * (M.x - q.x), z.y); + } else { + ptq = (q.x + cq * (m.x - q.x), z.y); + ptp = (z.x + cp * (M.x - z.x), z.y); + } + } else { + // Calculate the maximum and minimum t values allowed for the + // parametric equation z + t * v + real mx = (m.x - z.x)/v.x, Mx = (M.x - z.x)/v.x; + real my = (m.y - z.y)/v.y, My = (M.y - z.y)/v.y; + real tmin = max(v.x > 0 ? mx : Mx, v.y > 0 ? my : My); + real tmax = min(v.x > 0 ? Mx : mx, v.y > 0 ? My : my); + pair pmin = z + tmin * v; + pair pmax = z + tmax * v; + if(tmin <= tmax) { + ptp = z + cp * tmin * v; + ptq = z + (cq == 0 ? v:tmax * v); + } + } + path g = ptp--ptq; + if (length(g)>0) + { + if(L.s != "") { + Label lL = L.copy(); + if(L.defaultposition) lL.position(Relative(.9)); + lL.p(p); + lL.out(opic, g); + } + g = pathModifier(g); + if(linetype(p).length == 0){ + pair m = midpoint(g); + pen tp; + tp = dirP ? p : addpenline(p); + draw(opic, pathModifier(m--ptp), tp); + tp = dirQ ? p : addpenline(p); + draw(opic, pathModifier(m--ptq), tp); + } else { + draw(opic, g, p); + } + marker.markroutine(opic, marker.f, g); + arrow(opic, g, p, NoMargin); + add(f, opic.fit()); + } + }); +} + +/*<asyxml><function type="void" signature="clipdraw(picture,Label,path,align,pen,arrowbar,arrowbar,real,real,Label,marker)"><code></asyxml>*/ +void clipdraw(picture pic = currentpicture, Label L = "", path g, + align align = NoAlign, pen p = currentpen, + arrowbar arrow = None, arrowbar bar = None, + real xmargin = 0, real ymargin = xmargin, + Label legend = "", marker marker = nomarker) +{/*<asyxml></code><documentation>Draw the path 'g' on 'pic' clipped to the bounding box of 'pic'.</documentation></function></asyxml>*/ + if(L.s != "") { + picture tmp; + label(tmp, L, g, p); + add(pic, tmp); + } + pic.add(new void (frame f, transform t, transform T, pair m, pair M) { + // Reduce the bounds by the size of the pen and the margins. + m += min(p) + (xmargin, ymargin); M -= max(p) + (xmargin, ymargin); + path bound = box(m, M); + picture tmp; + draw(tmp, "", t * T * g, align, p, arrow, bar, NoMargin, legend, marker); + clip(tmp, bound); + add(f, tmp.fit()); + }); +} + +/*<asyxml><function type="void" signature="distance(picture pic,Label,point,point,bool,real,pen,pen,arrow)"><code></asyxml>*/ +void distance(picture pic = currentpicture, Label L = "", point A, point B, + bool rotated = true, real offset = 3mm, + pen p = currentpen, pen joinpen = invisible, + arrowbar arrow = Arrows(NoFill)) +{/*<asyxml></code><documentation>Draw arrow between A and B (from FAQ).</documentation></function></asyxml>*/ + pair A = A, B = B; + path g = A--B; + transform Tp = shift(-offset * unit(B - A) * I); + pic.add(new void(frame f, transform t) { + picture opic; + path G = Tp * t * g; + transform id = identity(); + transform T = rotated ? rotate(B - A) : id; + Label L = L.copy(); + L.align(L.align, Center); + if(abs(ypart((conj(A - B) * L.align.dir))) < epsgeo && L.filltype == NoFill) + L.filltype = UnFill(1); + draw(opic, T * L, G, p, arrow, Bars, PenMargins); + pair Ap = t * A, Bp = t * B; + draw(opic, (Ap--Tp * Ap)^^(Bp--Tp * Bp), joinpen); + add(f, opic.fit()); + }, true); + pic.addBox(min(g), max(g), Tp * min(p), Tp * max(p)); +} + +/*<asyxml><variable type="real" signature="perpfactor"><code></asyxml>*/ +real perpfactor = 1;/*<asyxml></code><documentation>Factor for drawing perpendicular symbol.</documentation></variable></asyxml>*/ +/*<asyxml><function type="void" signature="perpendicularmark(picture,point,explicit pair,explicit pair,real,pen,margin,filltype)"><code></asyxml>*/ +void perpendicularmark(picture pic = currentpicture, point z, + explicit pair align, + explicit pair dir = E, real size = 0, + pen p = currentpen, + margin margin = NoMargin, + filltype filltype = NoFill) +{/*<asyxml></code><documentation>Draw a perpendicular symbol at z aligned in the direction align + relative to the path z--z + dir. + dir(45 + n * 90), where n in N*, are common values for 'align'.</documentation></function></asyxml>*/ + p = squarecap + p; + if(size == 0) size = perpfactor * 3mm + sqrt(1 + linewidth(p)) - 1; + frame apic; + pair d1 = size * align * unit(dir) * dir(-45); + pair d2 = I * d1; + path g = d1--d1 + d2--d2; + g = margin(g, p).g; + draw(apic, g, p); + if(filltype != NoFill) filltype.fill(apic, (relpoint(g, 0) - relpoint(g, 0.5)+ + relpoint(g, 1))--g--cycle, p + solid); + add(pic, apic, locate(z)); +} + +/*<asyxml><function type="void" signature="perpendicularmark(picture,point,vector,vector,real,pen,margin,filltype)"><code></asyxml>*/ +void perpendicularmark(picture pic = currentpicture, point z, + vector align, + vector dir = E, real size = 0, + pen p = currentpen, + margin margin = NoMargin, + filltype filltype = NoFill) +{/*<asyxml></code><documentation>Draw a perpendicular symbol at z aligned in the direction align + relative to the path z--z + dir. + dir(45 + n * 90), where n in N, are common values for 'align'.</documentation></function></asyxml>*/ + perpendicularmark(pic, z, (pair)align, (pair)dir, size, + p, margin, filltype); +} + +/*<asyxml><function type="void" signature="perpendicularmark(picture,point,explicit pair,path,real,pen,margin,filltype)"><code></asyxml>*/ +void perpendicularmark(picture pic = currentpicture, point z, explicit pair align, path g, + real size = 0, pen p = currentpen, + margin margin = NoMargin, + filltype filltype = NoFill) +{/*<asyxml></code><documentation>Draw a perpendicular symbol at z aligned in the direction align + relative to the path z--z + dir(g, 0). + dir(45 + n * 90), where n in N, are common values for 'align'.</documentation></function></asyxml>*/ + perpendicularmark(pic, z, align, dir(g, 0), size, p, margin, filltype); +} + +/*<asyxml><function type="void" signature="perpendicularmark(picture,point,vector,path,real,pen,margin,filltype)"><code></asyxml>*/ +void perpendicularmark(picture pic = currentpicture, point z, vector align, path g, + real size = 0, pen p = currentpen, + margin margin = NoMargin, + filltype filltype = NoFill) +{/*<asyxml></code><documentation>Draw a perpendicular symbol at z aligned in the direction align + relative to the path z--z + dir(g, 0). + dir(45 + n * 90), where n in N, are common values for 'align'.</documentation></function></asyxml>*/ + perpendicularmark(pic, z, (pair)align, dir(g, 0), size, p, margin, filltype); +} + +/*<asyxml><function type="void" signature="markrightangle(picture,point,point,point,real,pen,margin,filltype)"><code></asyxml>*/ +void markrightangle(picture pic = currentpicture, point A, point O, + point B, real size = 0, pen p = currentpen, + margin margin = NoMargin, + filltype filltype = NoFill) +{/*<asyxml></code><documentation>Mark the angle AOB with a perpendicular symbol.</documentation></function></asyxml>*/ + pair Ap = A, Bp = B, Op = O; + pair dir = Ap - Op; + real a1 = degrees(dir); + pair align = rotate(-a1) * unit(dir(Op--Ap, Op--Bp)); + if (margin == NoMargin) + margin = TrueMargin(linewidth(currentpen)/2, linewidth(currentpen)/2); + perpendicularmark(pic = pic, z = O, align = align, + dir = dir, size = size, p = p, + margin = margin, filltype = filltype); +} + +/*<asyxml><function type="bool" signature="simeq(point,point,real)"><code></asyxml>*/ +bool simeq(point A, point B, real fuzz = epsgeo) +{/*<asyxml></code><documentation>Return true iff abs(A - B) < fuzz. + This routine is used internally to know if two points are equal, in particular by the operator == in 'point == point'.</documentation></function></asyxml>*/ + return (abs(A - B) < fuzz); +} +bool simeq(point a, real b, real fuzz = epsgeo) +{ + coordsys R = a.coordsys; + return (abs(a - point(R, ((pair)b)/R)) < fuzz); +} + +/*<asyxml><function type="pair" signature="attract(pair,path,real)"><code></asyxml>*/ +pair attract(pair m, path g, real fuzz = 0) +{/*<asyxml></code><documentation>Return the nearest point (A PAIR) of 'm' which is on the path g. + 'fuzz' is the argument 'fuzz' of 'intersect'.</documentation></function></asyxml>*/ + if(intersect(m, g, fuzz).length > 0) return m; + pair p; + real step = 1, r = 0; + real[] t; + static real eps = sqrt(realEpsilon); + do {// Find a radius for intersection + r += step; + t = intersect(shift(m) * scale(r) * unitcircle, g); + } while(t.length <= 0); + p = point(g, t[1]); + real rm = 0, rM = r; + while(rM - rm > eps) { + r = (rm + rM)/2; + t = intersect(shift(m) * scale(r) * unitcircle, g, fuzz); + if(t.length <= 0) { + rm = r; + } else { + rM = r; + p = point(g, t[1]); + } + } + return p; +} + +/*<asyxml><function type="point" signature="attract(point,path,real)"><code></asyxml>*/ +point attract(point M, path g, real fuzz = 0) +{/*<asyxml></code><documentation>Return the nearest point (A POINT) of 'M' which is on the path g. + 'fuzz' is the argument 'fuzz' of 'intersect'.</documentation></function></asyxml>*/ + return point(M.coordsys, attract(locate(M), g)/M.coordsys); +} + +/*<asyxml><function type="real[]" signature="intersect(path,explicit pair)"><code></asyxml>*/ +real[] intersect(path g, explicit pair p, real fuzz = 0) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + fuzz = fuzz <= 0 ? sqrt(realEpsilon) : fuzz; + real[] or; + real r = realEpsilon; + do{ + or = intersect(g, shift(p) * scale(r) * unitcircle, fuzz); + r *= 2; + } while(or.length == 0); + return or; +} + +/*<asyxml><function type="real[]" signature="intersect(path,explicit point)"><code></asyxml>*/ +real[] intersect(path g, explicit point P, real fuzz = epsgeo) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return intersect(g, locate(P), fuzz); +} +// *.........................BASES.........................* +// *=======================================================* + +// *=======================================================* +// *.........................LINES.........................* +/*<asyxml><struct signature="line"><code></asyxml>*/ +struct line +{/*<asyxml></code><documentation>This structure provides the objects line, semi - line and segment oriented from A to B. + All the calculus with this structure will be as exact as Asymptote can do. + For a full precision, you must not cast 'line' to 'path' excepted for drawing routines.</documentation></asyxml>*/ + /*<asyxml><property type = "point" signature="A,B"><code></asyxml>*/ + restricted point A,B;/*<asyxml></code><documentation>Two line's points with same coordinate system.</documentation></property><property type = "bool" signature="extendA,extendB"><code></asyxml>*/ + bool extendA,extendB;/*<asyxml></code><documentation>If true,extend 'l' in direction of A (resp. B).</documentation></property><property type = "vector" signature="u,v"><code></asyxml>*/ + restricted vector u,v;/*<asyxml></code><documentation>u = unit(AB) = direction vector,v = normal vector.</documentation></property><property type = "real" signature="a,b,c"><code></asyxml>*/ + restricted real a,b,c;/*<asyxml></code><documentation>Coefficients of the equation ax + by + c = 0 in the coordinate system of 'A'.</documentation></property><property type = "real" signature="slope,origin"><code></asyxml>*/ + restricted real slope, origin;/*<asyxml></code><documentation>Slope and ordinate at the origin.</documentation></property></asyxml>*/ + /*<asyxml><method type = "line" signature="copy()"><code></asyxml>*/ + line copy() + {/*<asyxml></code><documentation>Copy a line in a new instance.</documentation></method></asyxml>*/ + line l = new line; + l.A = A; + l.B = B; + l.a = a; + l.b = b; + l.c = c; + l.slope = slope; + l.origin = origin; + l.u = u; + l.v = v; + l.extendA = extendA; + l.extendB = extendB; + return l; + } + + /*<asyxml><method type = "void" signature="init(point,bool,point,bool)"><code></asyxml>*/ + void init(point A, bool extendA = true, point B, bool extendB = true) + {/*<asyxml></code><documentation>Initialize line. + If 'extendA' is true, the "line" is infinite in the direction of A.</documentation></method></asyxml>*/ + point[] P = standardizecoordsys(A, B); + this.A = P[0]; + this.B = P[1]; + this.a = B.y - A.y; + this.b = A.x - B.x; + this.c = A.y * B.x - A.x * B.y; + this.slope= (this.b == 0) ? infinity : -this.a/this.b; + this.origin = (this.b == 0) ? (this.c == 0) ? 0:infinity : -this.c/this.b; + this.u = unit(P[1]-P[0]); + // int tmp = sgnd(this.slope); + // this.u = (dot((pair)this.u, N) >= 0) ? tmp * this.u : -tmp * this.u; + this.v = rotate(90, point(P[0].coordsys, (0, 0))) * this.u; + this.extendA = extendA; + this.extendB = extendB; + } +}/*<asyxml></struct></asyxml>*/ + +/*<asyxml><function type="line" signature="line(point,bool,point,bool)"><code></asyxml>*/ +line line(point A, bool extendA = true, point B, bool extendB = true) +{/*<asyxml></code><documentation>Return the line passing through 'A' and 'B'. + If 'extendA' is true, the "line" is infinite in the direction of A. + A "line" can be half-line or segment.</documentation></function></asyxml>*/ + if (A == B) abort("line: the points must be distinct."); + line l; + l.init(A, extendA, B, extendB); + return l; +} + +/*<asyxml><struct signature="segment"><code></asyxml>*/ +struct segment +{/*<asyxml></code><documentation><look href = "struct line"/>.</documentation></asyxml>*/ + restricted point A, B;// Extremity. + restricted vector u, v;// u = direction vector, v = normal vector. + restricted real a, b, c;// Coefficients of the équation ax + by + c = 0 + restricted real slope, origin; + segment copy() + { + segment s = new segment; + s.A = A; + s.B = B; + s.a = a; + s.b = b; + s.c = c; + s.slope = slope; + s.origin = origin; + s.u = u; + s.v = v; + return s; + } + + void init(point A, point B) + { + line l; + l.init(A, B); + this.A = l.A; this.B = l.B; + this.a = l.a; this.b = l.b; this.c = l.c; + this.slope = l.slope; this.origin = l.origin; + this.u = l.u; this.v = l.v; + } +}/*<asyxml></struct></asyxml>*/ + +/*<asyxml><function type="segment" signature="segment(point,point)"><code></asyxml>*/ +segment segment(point A, point B) +{/*<asyxml></code><documentation>Return the segment whose the extremities are A and B.</documentation></function></asyxml>*/ + segment s; + s.init(A, B); + return s; +} + +/*<asyxml><function type="real" signature="length(segment)"><code></asyxml>*/ +real length(segment s) +{/*<asyxml></code><documentation>Return the length of 's'.</documentation></function></asyxml>*/ + return abs(s.A - s.B); +} + +/*<asyxml><operator type = "line" signature="cast(segment)"><code></asyxml>*/ +line operator cast(segment s) +{/*<asyxml></code><documentation>A segment is casted to a "finite line".</documentation></operator></asyxml>*/ + return line(s.A, false, s.B, false); +} + +/*<asyxml><operator type = "segment" signature="cast(line)"><code></asyxml>*/ +segment operator cast(line l) +{/*<asyxml></code><documentation>Cast line 'l' to segment [l.A l.B].</documentation></operator></asyxml>*/ + return segment(l.A, l.B); +} + +/*<asyxml><operator type = "line" signature="*(transform,line)"><code></asyxml>*/ +line operator *(transform t, line l) +{/*<asyxml></code><documentation>Provide transform * line</documentation></operator></asyxml>*/ + return line(t * l.A, l.extendA, t * l.B, l.extendB); +} +/*<asyxml><operator type = "line" signature="/(line,real)"><code></asyxml>*/ +line operator /(line l, real x) +{/*<asyxml></code><documentation>Provide l/x. + Return the line passing through l.A/x and l.B/x.</documentation></operator></asyxml>*/ + return line(l.A/x, l.extendA, l.B/x, l.extendB); +} +line operator /(line l, int x){return line(l.A/x, l.B/x);} +/*<asyxml><operator type = "line" signature="*(real,line)"><code></asyxml>*/ +line operator *(real x, line l) +{/*<asyxml></code><documentation>Provide x * l. + Return the line passing through x * l.A and x * l.B.</documentation></operator></asyxml>*/ + return line(x * l.A, l.extendA, x * l.B, l.extendB); +} +line operator *(int x, line l){return line(x * l.A, l.extendA, x * l.B, l.extendB);} + +/*<asyxml><operator type = "line" signature="*(point,line)"><code></asyxml>*/ +line operator *(point M, line l) +{/*<asyxml></code><documentation>Provide point * line. + Return the line passing through unit(M) * l.A and unit(M) * l.B.</documentation></operator></asyxml>*/ + return line(unit(M) * l.A, l.extendA, unit(M) * l.B, l.extendB); +} +/*<asyxml><operator type = "line" signature="+(line,point)"><code></asyxml>*/ +line operator +(line l, vector u) +{/*<asyxml></code><documentation>Provide line + vector (and so line + point). + Return the line 'l' shifted by 'u'.</documentation></operator></asyxml>*/ + return line(l.A + u, l.extendA, l.B + u, l.extendB); +} +/*<asyxml><operator type = "line" signature="-(line,vector)"><code></asyxml>*/ +line operator -(line l, vector u) +{/*<asyxml></code><documentation>Provide line - vector (and so line - point). + Return the line 'l' shifted by '-u'.</documentation></operator></asyxml>*/ + return line(l.A - u, l.extendA, l.B - u, l.extendB); +} + +/*<asyxml><operator type = "line[]" signature="^^(line,line)"><code></asyxml>*/ +line[] operator ^^(line l1, line l2) +{/*<asyxml></code><documentation>Provide line^^line. + Return the line array {l1, l2}.</documentation></operator></asyxml>*/ + line[] ol; + ol.push(l1); ol.push(l2); + return ol; +} + +/*<asyxml><operator type = "line[]" signature="^^(line,line[])"><code></asyxml>*/ +line[] operator ^^(line l1, line[] l2) +{/*<asyxml></code><documentation>Provide line^^line[]. + Return the line array {l1, l2[0], l2[1]...}. + line[]^^line is also defined.</documentation></operator></asyxml>*/ + line[] ol; + ol.push(l1); + for (int i = 0; i < l2.length; ++i) { + ol.push(l2[i]); + } + return ol; +} +line[] operator ^^(line[] l2, line l1) +{ + line[] ol = l2; + ol.push(l1); + return ol; +} + +/*<asyxml><operator type = "line[]" signature="^^(line,line[])"><code></asyxml>*/ +line[] operator ^^(line l1[], line[] l2) +{/*<asyxml></code><documentation>Provide line[]^^line[]. + Return the line array {l1[0], l1[1], ..., l2[0], l2[1], ...}.</documentation></operator></asyxml>*/ + line[] ol = l1; + for (int i = 0; i < l2.length; ++i) { + ol.push(l2[i]); + } + return ol; +} + +/*<asyxml><function type="bool" signature="sameside(point,point,line)"><code></asyxml>*/ +bool sameside(point M, point P, line l) +{/*<asyxml></code><documentation>Return 'true' iff 'M' and 'N' are same side of the line (or on the line) 'l'.</documentation></function></asyxml>*/ + pair A = l.A, B = l.B, m = M, p = P; + pair mil = (A + B)/2; + pair mA = rotate(90, mil) * A; + pair mB = rotate(-90, mil) * A; + return (abs(m - mA) <= abs(m - mB)) == (abs(p - mA) <= abs(p - mB)); + // transform proj = projection(l.A, l.B); + // point Mp = proj * M; + // point Pp = proj * P; + // dot(Mp);dot(Pp); + // return dot(locate(Mp - M), locate(Pp - P)) >= 0; +} + +/*<asyxml><function type="line" signature="line(segment)"><code></asyxml>*/ +line line(segment s) +{/*<asyxml></code><documentation>Return the line passing through 's.A' + and 's.B'.</documentation></function></asyxml>*/ + return line(s.A, s.B); +} +/*<asyxml><function type="segment" signature="segment(line)"><code></asyxml>*/ +segment segment(line l) +{/*<asyxml></code><documentation>Return the segment whose extremities + are 'l.A' and 'l.B'.</documentation></function></asyxml>*/ + return segment(l.A, l.B); +} + +/*<asyxml><function type="point" signature="midpoint(segment)"><code></asyxml>*/ +point midpoint(segment s) +{/*<asyxml></code><documentation>Return the midpoint of 's'.</documentation></function></asyxml>*/ + return 0.5 * (s.A + s.B); +} + +/*<asyxml><function type="void" signature="write(line)"><code></asyxml>*/ +void write(explicit line l) +{/*<asyxml></code><documentation>Write some informations about 'l'.</documentation></function></asyxml>*/ + write("A = "+(string)((pair)l.A)); + write("Extend A = "+(l.extendA ? "true" : "false")); + write("B = "+(string)((pair)l.B)); + write("Extend B = "+(l.extendB ? "true" : "false")); + write("u = "+(string)((pair)l.u)); + write("v = "+(string)((pair)l.v)); + write("a = "+(string) l.a); + write("b = "+(string) l.b); + write("c = "+(string) l.c); + write("slope = "+(string) l.slope); + write("origin = "+(string) l.origin); +} + +/*<asyxml><function type="void" signature="write(explicit segment)"><code></asyxml>*/ +void write(explicit segment s) +{/*<asyxml></code><documentation>Write some informations about 's'.</documentation></function></asyxml>*/ + write("A = "+(string)((pair)s.A)); + write("B = "+(string)((pair)s.B)); + write("u = "+(string)((pair)s.u)); + write("v = "+(string)((pair)s.v)); + write("a = "+(string) s.a); + write("b = "+(string) s.b); + write("c = "+(string) s.c); + write("slope = "+(string) s.slope); + write("origin = "+(string) s.origin); +} + +/*<asyxml><operator type = "bool" signature="==(line,line)"><code></asyxml>*/ +bool operator ==(line l1, line l2) +{/*<asyxml></code><documentation>Provide the test 'line == line'.</documentation></operator></asyxml>*/ + return (collinear(l1.u, l2.u) && + abs(ypart((locate(l1.A) - locate(l1.B))/(locate(l1.A) - locate(l2.B)))) < epsgeo && + l1.extendA == l2.extendA && l1.extendB == l2.extendB); +} + +/*<asyxml><operator type = "bool" signature="!=(line,line)"><code></asyxml>*/ +bool operator !=(line l1, line l2) +{/*<asyxml></code><documentation>Provide the test 'line != line'.</documentation></operator></asyxml>*/ + return !(l1 == l2); +} + +/*<asyxml><operator type = "bool" signature="@(point,line)"><code></asyxml>*/ +bool operator @(point m, line l) +{/*<asyxml></code><documentation>Provide the test 'point @ line'. + Return true iff 'm' is on the 'l'.</documentation></operator></asyxml>*/ + point M = changecoordsys(l.A.coordsys, m); + if (abs(l.a * M.x + l.b * M.y + l.c) >= epsgeo) return false; + if (l.extendA && l.extendB) return true; + if (!l.extendA && !l.extendB) return between(l.A, M, l.B); + if (l.extendA) return sameside(M, l.A, l.B); + return sameside(M, l.B, l.A); +} + +/*<asyxml><function type="coordsys" signature="coordsys(line)"><code></asyxml>*/ +coordsys coordsys(line l) +{/*<asyxml></code><documentation>Return the coordinate system in which 'l' is defined.</documentation></function></asyxml>*/ + return l.A.coordsys; +} + +/*<asyxml><function type="line" signature="reverse(line)"><code></asyxml>*/ +line reverse(line l) +{/*<asyxml></code><documentation>Permute the points 'A' and 'B' of 'l' and so its orientation.</documentation></function></asyxml>*/ + return line(l.B, l.extendB, l.A, l.extendA); +} + +/*<asyxml><function type="line" signature="extend(line)"><code></asyxml>*/ +line extend(line l) +{/*<asyxml></code><documentation>Return the infinite line passing through 'l.A' and 'l.B'.</documentation></function></asyxml>*/ + line ol = l.copy(); + ol.extendA = true; + ol.extendB = true; + return ol; +} + +/*<asyxml><function type="line" signature="complementary(explicit line)"><code></asyxml>*/ +line complementary(explicit line l) +{/*<asyxml></code><documentation>Return the complementary of a half-line with respect of + the full line 'l'.</documentation></function></asyxml>*/ + if (l.extendA && l.extendB) + abort("complementary: the parameter is not a half-line."); + point origin = l.extendA ? l.B : l.A; + point ptdir = l.extendA ? + rotate(180, l.B) * l.A : rotate(180, l.A) * l.B; + return line(origin, false, ptdir); +} + +/*<asyxml><function type="line[]" signature="complementary(explicit segment)"><code></asyxml>*/ +line[] complementary(explicit segment s) +{/*<asyxml></code><documentation>Return the two half-lines of origin 's.A' and 's.B' respectively.</documentation></function></asyxml>*/ + line[] ol = new line[2]; + ol[0] = complementary(line(s.A, false, s.B)); + ol[1] = complementary(line(s.A, s.B, false)); + return ol; +} + +/*<asyxml><function type="line" signature="Ox(coordsys)"><code></asyxml>*/ +line Ox(coordsys R = currentcoordsys) +{/*<asyxml></code><documentation>Return the x-axis of 'R'.</documentation></function></asyxml>*/ + return line(point(R, (0, 0)), point(R, E)); +} +/*<asyxml><constant type = "line" signature="Ox"><code></asyxml>*/ +restricted line Ox = Ox();/*<asyxml></code><documentation>the x-axis of + the default coordinate system.</documentation></constant></asyxml>*/ + +/*<asyxml><function type="line" signature="Oy(coordsys)"><code></asyxml>*/ +line Oy(coordsys R = currentcoordsys) +{/*<asyxml></code><documentation>Return the y-axis of 'R'.</documentation></function></asyxml>*/ + return line(point(R, (0, 0)), point(R, N)); +} +/*<asyxml><constant type = "line" signature="Oy"><code></asyxml>*/ +restricted line Oy = Oy();/*<asyxml></code><documentation>the y-axis of + the default coordinate system.</documentation></constant></asyxml>*/ + +/*<asyxml><function type="line" signature="line(real,point)"><code></asyxml>*/ +line line(real a, point A = point(currentcoordsys, (0, 0))) +{/*<asyxml></code><documentation>Return the line passing through 'A' with an + angle (in the coordinate system of A) 'a' in degrees. + line(point, real) is also defined.</documentation></function></asyxml>*/ + return line(A, A + point(A.coordsys, A.coordsys.polar(1, radians(a)))); +} +line line(point A = point(currentcoordsys, (0, 0)), real a) +{ + return line(a, A); +} +line line(int a, point A = point(currentcoordsys, (0, 0))) +{ + return line((real)a, A); +} + +/*<asyxml><function type="line" signature="line(coordsys,real,real)"><code></asyxml>*/ +line line(coordsys R = currentcoordsys, real slope, real origin) +{/*<asyxml></code><documentation>Return the line defined by slope and y-intercept relative to 'R'.</documentation></function></asyxml>*/ + if (slope == infinity || slope == -infinity) + abort("The slope is infinite. Please, use the routine 'vline'."); + return line(point(R, (0, origin)), point(R, (1, origin + slope))); +} + +/*<asyxml><function type="line" signature="line(coordsys,real,real,real)"><code></asyxml>*/ +line line(coordsys R = currentcoordsys, real a, real b, real c) +{/*<asyxml></code><documentation>Retrun the line defined by equation relative to 'R'.</documentation></function></asyxml>*/ + if (a == 0 && b == 0) abort("line: inconsistent equation..."); + pair M; + M = (a == 0) ? (0, -c/b) : (-c/a, 0); + return line(point(R, M), point(R, M + (-b, a))); +} + +/*<asyxml><function type="line" signature="vline(coordsys)"><code></asyxml>*/ +line vline(coordsys R = currentcoordsys) +{/*<asyxml></code><documentation>Return a vertical line in 'R' passing through the origin of 'R'.</documentation></function></asyxml>*/ + point P = point(R, (0, 0)); + point PP = point(R, (R.O + N)/R); + return line(P, PP); +} +/*<asyxml><constant type = "line" signature="vline"><code></asyxml>*/ +restricted line vline = vline();/*<asyxml></code><documentation>The vertical line in the current coordinate system passing + through the origin of this system.</documentation></constant></asyxml>*/ + +/*<asyxml><function type="line" signature="hline(coordsys)"><code></asyxml>*/ +line hline(coordsys R = currentcoordsys) +{/*<asyxml></code><documentation>Return a horizontal line in 'R' passing through the origin of 'R'.</documentation></function></asyxml>*/ + point P = point(R, (0, 0)); + point PP = point(R, (R.O + E)/R); + return line(P, PP); +} +/*<asyxml><constant type = "line" signature="hline"><code></asyxml>*/ +line hline = hline();/*<asyxml></code><documentation>The horizontal line in the current coordinate system passing + through the origin of this system.</documentation></constant></asyxml>*/ + +/*<asyxml><function type="line" signature="changecoordsys(coordsys,line)"><code></asyxml>*/ +line changecoordsys(coordsys R, line l) +{/*<asyxml></code><documentation>Return the line 'l' in the coordinate system 'R'.</documentation></function></asyxml>*/ + point A = changecoordsys(R, l.A); + point B = changecoordsys(R, l.B); + return line(A, B); +} + +/*<asyxml><function type="transform" signature="scale(real,line,line,bool)"><code></asyxml>*/ +transform scale(real k, line l1, line l2, bool safe = false) +{/*<asyxml></code><documentation>Return the dilatation with respect to + 'l1' in the direction of 'l2'.</documentation></function></asyxml>*/ + return scale(k, l1.A, l1.B, l2.A, l2.B, safe); +} + +/*<asyxml><function type="transform" signature="reflect(line)"><code></asyxml>*/ +transform reflect(line l) +{/*<asyxml></code><documentation>Return the reflect about the line 'l'.</documentation></function></asyxml>*/ + return reflect((pair)l.A, (pair)l.B); +} + +/*<asyxml><function type="transform" signature="reflect(line,line)"><code></asyxml>*/ +transform reflect(line l1, line l2, bool safe = false) +{/*<asyxml></code><documentation>Return the reflect about the line + 'l1' in the direction of 'l2'.</documentation></function></asyxml>*/ + return scale(-1.0, l1, l2, safe); +} + + +/*<asyxml><function type="point[]" signature="intersectionpoints(line,path)"><code></asyxml>*/ +point[] intersectionpoints(line l, path g) +{/*<asyxml></code><documentation>Return all points of intersection of the line 'l' with the path 'g'.</documentation></function></asyxml>*/ + // TODO utiliser la version 1.44 de intersections(path g, pair p, pair q) + // real [] t = intersections(g, l.A, l.B); + // coordsys R = coordsys(l); + // return sequence(new point(int n){return point(R, point(g, t[n])/R);}, t.length); + real [] t; + pair[] op; + pair A = l.A; + pair B = l.B; + real dy = B.y - A.y, + dx = A.x - B.x, + lg = length(g); + + for (int i = 0; i < lg; ++i) + { + pair z0 = point(g, i), + z1 = point(g, i + 1), + c0 = postcontrol(g, i), + c1 = precontrol(g, i + 1), + t3 = z1 - z0 - 3 * c1 + 3 * c0, + t2 = 3 * z0 + 3 * c1 - 6 * c0, + t1 = 3 * c0 - 3z0; + real a = dy * t3.x + dx * t3.y, + b = dy * t2.x + dx * t2.y, + c = dy * t1.x + dx * t1.y, + d = dy * z0.x + dx * z0.y + A.y * B.x - A.x * B.y; + + t = cubicroots(a, b, c, d); + for (int j = 0; j < t.length; ++j) + if ( + t[j]>=0 + && ( + t[j]<1 + || ( + t[j] == 1 + && (i == lg - 1) + && !cyclic(g) + ) + ) + ) { + op.push(point(g, i + t[j])); + } + } + + point[] opp; + for (int i = 0; i < op.length; ++i) + opp.push(point(coordsys(l), op[i]/coordsys(l))); + return opp; +} + +/*<asyxml><function type="point" signature="intersectionpoint(line,line)"><code></asyxml>*/ +point intersectionpoint(line l1, line l2) +{/*<asyxml></code><documentation>Return the point of intersection of line 'l1' with 'l2'. + If 'l1' and 'l2' have an infinity or none point of intersection, + this routine return (infinity, infinity).</documentation></function></asyxml>*/ + point[] P = standardizecoordsys(l1.A, l1.B, l2.A, l2.B); + coordsys R = P[0].coordsys; + pair p = extension(P[0], P[1], P[2], P[3]); + if(finite(p)){ + point p = point(R, p/R); + if (p @ l1 && p @ l2) return p; + } + return point(R, (infinity, infinity)); +} + +/*<asyxml><function type="line" signature="parallel(point,line)"><code></asyxml>*/ +line parallel(point M, line l) +{/*<asyxml></code><documentation>Return the line parallel to 'l' passing through 'M'.</documentation></function></asyxml>*/ + point A, B; + if (M.coordsys != coordsys(l)) + { + A = changecoordsys(M.coordsys, l.A); + B = changecoordsys(M.coordsys, l.B); + } else {A = l.A;B = l.B;} + return line(M, M - A + B); +} + +/*<asyxml><function type="line" signature="parallel(point,explicit vector)"><code></asyxml>*/ +line parallel(point M, explicit vector dir) +{/*<asyxml></code><documentation>Return the line of direction 'dir' and passing through 'M'.</documentation></function></asyxml>*/ + return line(M, M + locate(dir)); +} + +/*<asyxml><function type="line" signature="parallel(point,explicit pair)"><code></asyxml>*/ +line parallel(point M, explicit pair dir) +{/*<asyxml></code><documentation>Return the line of direction 'dir' and passing through 'M'.</documentation></function></asyxml>*/ + return line(M, M + vector(currentcoordsys, dir)); +} + +/*<asyxml><function type="bool" signature="parallel(line,line)"><code></asyxml>*/ +bool parallel(line l1, line l2, bool strictly = false) +{/*<asyxml></code><documentation>Return 'true' if 'l1' and 'l2' are (strictly ?) parallel.</documentation></function></asyxml>*/ + bool coll = collinear(l1.u, l2.u); + return strictly ? coll && (l1 != l2) : coll; +} + +/*<asyxml><function type="bool" signature="concurrent(...line[])"><code></asyxml>*/ +bool concurrent(... line[] l) +{/*<asyxml></code><documentation>Returns true if all the lines 'l' are concurrent.</documentation></function></asyxml>*/ + if (l.length < 3) abort("'concurrent' needs at least for three lines ..."); + pair point = intersectionpoint(l[0], l[1]); + bool conc; + for (int i = 2; i < l.length; ++i) { + pair pt = intersectionpoint(l[i - 1], l[i]); + conc = simeq(pt, point); + if (!conc) break; + } + return conc; +} + +/*<asyxml><function type="transform" signature="projection(line)"><code></asyxml>*/ +transform projection(line l) +{/*<asyxml></code><documentation>Return the orthogonal projection on 'l'.</documentation></function></asyxml>*/ + return projection(l.A, l.B); +} + +/*<asyxml><function type="transform" signature="projection(line,line,bool)"><code></asyxml>*/ +transform projection(line l1, line l2, bool safe = false) +{/*<asyxml></code><documentation>Return the projection on (AB) in parallel of (CD). + If 'safe = true' and (l1)//(l2) return the identity. + If 'safe = false' and (l1)//(l2) return a infinity scaling.</documentation></function></asyxml>*/ + return projection(l1.A, l1.B, l2.A, l2.B, safe); +} + +/*<asyxml><function type="transform" signature="vprojection(line,bool)"><code></asyxml>*/ +transform vprojection(line l, bool safe = false) +{/*<asyxml></code><documentation>Return the projection on 'l' in parallel of N--S. + If 'safe' is 'true' the projected point keeps the same place if 'l' + is vertical.</documentation></function></asyxml>*/ + coordsys R = defaultcoordsys; + return projection(l, line(point(R, N), point(R, S)), safe); +} + +/*<asyxml><function type="transform" signature="hprojection(line,bool)"><code></asyxml>*/ +transform hprojection(line l, bool safe = false) +{/*<asyxml></code><documentation>Return the projection on 'l' in parallel of E--W. + If 'safe' is 'true' the projected point keeps the same place if 'l' + is horizontal.</documentation></function></asyxml>*/ + coordsys R = defaultcoordsys; + return projection(l, line(point(R, E), point(R, W)), safe); +} + +/*<asyxml><function type="line" signature="perpendicular(point,line)"><code></asyxml>*/ +line perpendicular(point M, line l) +{/*<asyxml></code><documentation>Return the perpendicular line of 'l' passing through 'M'.</documentation></function></asyxml>*/ + point Mp = projection(l) * M; + point A = Mp == l.A ? l.B : l.A; + return line(Mp, rotate(90, Mp) * A); +} + +/*<asyxml><function type="line" signature="perpendicular(point,explicit vector)"><code></asyxml>*/ +line perpendicular(point M, explicit vector normal) +{/*<asyxml></code><documentation>Return the line passing through 'M' + whose normal is \param{normal}.</documentation></function></asyxml>*/ + return perpendicular(M, line(M, M + locate(normal))); +} + +/*<asyxml><function type="line" signature="perpendicular(point,explicit pair)"><code></asyxml>*/ +line perpendicular(point M, explicit pair normal) +{/*<asyxml></code><documentation>Return the line passing through 'M' + whose normal is \param{normal} (given in the currentcoordsys).</documentation></function></asyxml>*/ + return perpendicular(M, line(M, M + vector(currentcoordsys, normal))); +} + +/*<asyxml><function type="bool" signature="perpendicular(line,line)"><code></asyxml>*/ +bool perpendicular(line l1, line l2) +{/*<asyxml></code><documentation>Return 'true' if 'l1' and 'l2' are perpendicular.</documentation></function></asyxml>*/ + return abs(dot(locate(l1.u), locate(l2.u))) < epsgeo ; +} + +/*<asyxml><function type="real" signature="angle(line,coordsys)"><code></asyxml>*/ +real angle(line l, coordsys R = coordsys(l)) +{/*<asyxml></code><documentation>Return the angle of the oriented line 'l', + in radian, in the interval ]-pi, pi] and relatively to 'R'.</documentation></function></asyxml>*/ + return angle(l.u, R, false); +} + +/*<asyxml><function type="real" signature="degrees(line,coordsys,bool)"><code></asyxml>*/ +real degrees(line l, coordsys R = coordsys(l)) +{/*<asyxml></code><documentation>Returns the angle of the oriented line 'l' in degrees, + in the interval [0, 360[ and relatively to 'R'.</documentation></function></asyxml>*/ + return degrees(angle(l, R)); +} + +/*<asyxml><function type="real" signature="sharpangle(line,line)"><code></asyxml>*/ +real sharpangle(line l1, line l2) +{/*<asyxml></code><documentation>Return the measure in radians of the sharp angle formed by 'l1' and 'l2'.</documentation></function></asyxml>*/ + vector u1 = l1.u; + vector u2 = (dot(l1.u, l2.u) < 0) ? -l2.u : l2.u; + real a12 = angle(locate(u2)) - angle(locate(u1)); + a12 = a12%(sgnd(a12) * pi); + if (a12 <= -pi/2) { + a12 += pi; + } else if (a12 > pi/2) { + a12 -= pi; + } + return a12; +} + +/*<asyxml><function type="real" signature="angle(line,line)"><code></asyxml>*/ +real angle(line l1, line l2) +{/*<asyxml></code><documentation>Return the measure in radians of oriented angle (l1.u, l2.u).</documentation></function></asyxml>*/ + return angle(locate(l2.u)) - angle(locate(l1.u)); +} + +/*<asyxml><function type="real" signature="degrees(line,line)"><code></asyxml>*/ +real degrees(line l1, line l2) +{/*<asyxml></code><documentation>Return the measure in degrees of the + angle formed by the oriented lines 'l1' and 'l2'.</documentation></function></asyxml>*/ + return degrees(angle(l1, l2)); +} + +/*<asyxml><function type="real" signature="sharpdegrees(line,line)"><code></asyxml>*/ +real sharpdegrees(line l1, line l2) +{/*<asyxml></code><documentation>Return the measure in degrees of the sharp angle formed by 'l1' and 'l2'.</documentation></function></asyxml>*/ + return degrees(sharpangle(l1, l2)); +} + +/*<asyxml><function type="line" signature="bisector(line,line,real,bool)"><code></asyxml>*/ +line bisector(line l1, line l2, real angle = 0, bool sharp = true) +{/*<asyxml></code><documentation>Return the bisector of the angle formed by 'l1' and 'l2' + rotated by the angle 'angle' (in degrees) around intersection point of 'l1' with 'l2'. + If 'sharp' is true (the default), this routine returns the bisector of the sharp angle. + Note that the returned line inherit of coordinate system of 'l1'.</documentation></function></asyxml>*/ + line ol; + if (l1 == l2) return l1; + point A = intersectionpoint(l1, l2); + if (finite(A)) { + if(sharp) ol = rotate(sharpdegrees(l1, l2)/2 + angle, A) * l1; + else { + coordsys R = coordsys(l1); + pair a = A, b = A + l1.u, c = A + l2.u; + pair pp = extension(a, a + dir(a--b, a--c), b, b + dir(b--a, b--c)); + return rotate(angle, A) * line(A, point(R, pp/R)); + } + } else { + ol = l1; + } + return ol; +} + +/*<asyxml><function type="line" signature="sector(int,int,line,line,real,bool)"><code></asyxml>*/ +line sector(int n = 2, int p = 1, line l1, line l2, real angle = 0, bool sharp = true) +{/*<asyxml></code><documentation>Return the p-th nth-sector of the angle + formed by the oriented line 'l1' and 'l2' + rotated by the angle 'angle' (in degrees) around the intersection point of 'l1' with 'l2'. + If 'sharp' is true (the default), this routine returns the bisector of the sharp angle. + Note that the returned line inherit of coordinate system of 'l1'.</documentation></function></asyxml>*/ + line ol; + if (l1 == l2) return l1; + point A = intersectionpoint(l1, l2); + if (finite(A)) { + if(sharp) ol = rotate(p * sharpdegrees(l1, l2)/n + angle, A) * l1; + else { + ol = rotate(p * degrees(l1, l2)/n + angle, A) * l1; + } + } else { + ol = l1; + } + return ol; +} + +/*<asyxml><function type="line" signature="bisector(point,point,point,point,real)"><code></asyxml>*/ +line bisector(point A, point B, point C, point D, real angle = 0, bool sharp = true) +{/*<asyxml></code><documentation>Return the bisector of the angle formed by the lines (AB) and (CD). + <look href = "#bisector(line, line, real, bool)"/>.</documentation></function></asyxml>*/ + point[] P = standardizecoordsys(A, B, C, D); + return bisector(line(P[0], P[1]), line(P[2], P[3]), angle, sharp); +} + +/*<asyxml><function type="line" signature="bisector(segment,real)"><code></asyxml>*/ +line bisector(segment s, real angle = 0) +{/*<asyxml></code><documentation>Return the bisector of the segment line 's' rotated by 'angle' (in degrees) around the + midpoint of 's'.</documentation></function></asyxml>*/ + coordsys R = coordsys(s); + point m = midpoint(s); + vector dir = rotateO(90) * unit(s.A - m); + return rotate(angle, m) * line(m + dir, m - dir); +} + +/*<asyxml><function type="line" signature="bisector(point,point,real)"><code></asyxml>*/ +line bisector(point A, point B, real angle = 0) +{/*<asyxml></code><documentation>Return the bisector of the segment line [AB] rotated by 'angle' (in degrees) around the + midpoint of [AB].</documentation></function></asyxml>*/ + point[] P = standardizecoordsys(A, B); + return bisector(segment(P[0], P[1]), angle); +} + +/*<asyxml><function type="real" signature="distance(point,line)"><code></asyxml>*/ +real distance(point M, line l) +{/*<asyxml></code><documentation>Return the distance from 'M' to 'l'. + distance(line, point) is also defined.</documentation></function></asyxml>*/ + point A = changecoordsys(defaultcoordsys, l.A); + point B = changecoordsys(defaultcoordsys, l.B); + line ll = line(A, B); + pair m = locate(M); + return abs(ll.a * m.x + ll.b * m.y + ll.c)/sqrt(ll.a^2 + ll.b^2); +} + +real distance(line l, point M) +{ + return distance(M, l); +} + +/*<asyxml><function type="void" signature="draw(picture,Label,line,bool,bool,align,pen,arrowbar,Label,marker)"><code></asyxml>*/ +void draw(picture pic = currentpicture, Label L = "", + line l, bool dirA = l.extendA, bool dirB = l.extendB, + align align = NoAlign, pen p = currentpen, + arrowbar arrow = None, + Label legend = "", marker marker = nomarker, + pathModifier pathModifier = NoModifier) +{/*<asyxml></code><documentation>Draw the line 'l' without altering the size of picture pic. + The boolean parameters control the infinite section. + The global variable 'linemargin' (default value is 0) allows to modify + the bounding box in which the line must be drawn.</documentation></function></asyxml>*/ + if(!(dirA || dirB)) draw(l.A--l.B, invisible);// l is a segment. + Drawline(pic, L, l.A, dirP = dirA, l.B, dirQ = dirB, + align, p, arrow, + legend, marker, pathModifier); +} + +/*<asyxml><function type="void" signature="draw(picture,Label[], line[], align,pen[], arrowbar,Label,marker)"><code></asyxml>*/ +void draw(picture pic = currentpicture, Label[] L = new Label[], line[] l, + align align = NoAlign, pen[] p = new pen[], + arrowbar arrow = None, + Label[] legend = new Label[], marker marker = nomarker, + pathModifier pathModifier = NoModifier) +{/*<asyxml></code><documentation>Draw each lines with the corresponding pen.</documentation></function></asyxml>*/ + for (int i = 0; i < l.length; ++i) { + draw(pic, L.length>0 ? L[i] : "", l[i], + align, p = p.length>0 ? p[i] : currentpen, + arrow, legend.length>0 ? legend[i] : "", marker, + pathModifier); + } +} + +/*<asyxml><function type="void" signature="draw(picture,Label[], line[], align,pen,arrowbar,Label,marker)"><code></asyxml>*/ +void draw(picture pic = currentpicture, Label[] L = new Label[], line[] l, + align align = NoAlign, pen p, + arrowbar arrow = None, + Label[] legend = new Label[], marker marker = nomarker, + pathModifier pathModifier = NoModifier) +{/*<asyxml></code><documentation>Draw each lines with the same pen 'p'.</documentation></function></asyxml>*/ + pen[] tp = sequence(new pen(int i){return p;}, l.length); + draw(pic, L, l, align, tp, arrow, legend, marker, pathModifier); +} + +/*<asyxml><function type="void" signature="show(picture,line,pen)"><code></asyxml>*/ +void show(picture pic = currentpicture, line l, pen p = red) +{/*<asyxml></code><documentation>Draw some informations of 'l'.</documentation></function></asyxml>*/ + dot("$A$", (pair)l.A, align = -locate(l.v), p); + dot("$B$", (pair)l.B, align = -locate(l.v), p); + draw(l, dotted); + draw("$\vec{u}$", locate(l.A)--locate(l.A + l.u), p, Arrow); + draw("$\vec{v}$", locate(l.A)--locate(l.A + l.v), p, Arrow); +} + +/*<asyxml><function type="point[]" signature="sameside(point,line,line)"><code></asyxml>*/ +point[] sameside(point M, line l1, line l2) +{/*<asyxml></code><documentation>Return two points on 'l1' and 'l2' respectively. + The first point is from the same side of M relatively to 'l2', + the second point is from the same side of M relatively to 'l1'.</documentation></function></asyxml>*/ + point[] op; + coordsys R1 = coordsys(l1); + coordsys R2 = coordsys(l2); + if (parallel(l1, l2)) { + op.push(projection(l1) * M); + op.push(projection(l2) * M); + } else { + point O = intersectionpoint(l1, l2); + if (M @ l2) op.push((sameside(M, O + l1.u, l2)) ? O + l1.u : rotate(180, O) * (O + l1.u)); + else op.push(projection(l1, l2) * M); + if (M @ l1) op.push((sameside(M, O + l2.u, l1)) ? O + l2.u : rotate(180, O) * (O + l2.u)); + else {op.push(projection(l2, l1) * M);} + } + return op; +} + +/*<asyxml><function type="void" signature="markangle(picture,Label,int,real,real,explicit line,explicit line,explicit pair,arrowbar,pen,filltype,margin,marker)"><code></asyxml>*/ +void markangle(picture pic = currentpicture, + Label L = "", int n = 1, real radius = 0, real space = 0, + explicit line l1, explicit line l2, explicit pair align = dir(1), + arrowbar arrow = None, pen p = currentpen, + filltype filltype = NoFill, + margin margin = NoMargin, marker marker = nomarker) +{/*<asyxml></code><documentation>Mark the angle (l1, l2) aligned in the direction 'align' relative to 'l1'. + Commune values for 'align' are dir(real).</documentation></function></asyxml>*/ + if (parallel(l1, l2, true)) return; + real al = degrees(l1, defaultcoordsys); + pair O, A, B; + if (radius == 0) radius = markangleradius(p); + real d = degrees(locate(l1.u)); + align = rotate(d) * align; + if (l1 == l2) { + O = midpoint(segment(l1.A, l1.B)); + A = l1.A;B = l1.B; + if (sameside(rotate(sgn(angle(B-A)) * 45, O) * A, O + align, l1)) {radius = -radius;} + } else { + O = intersectionpoint(extend(l1), extend(l2)); + pair R = O + align; + point [] ss = sameside(point(coordsys(l1), R/coordsys(l1)), l1, l2); + A = ss[0]; + B = ss[1]; + } + markangle(pic = pic, L = L, n = n, radius = radius, space = space, + O = O, A = A, B = B, + arrow = arrow, p = p, filltype = filltype, + margin = margin, marker = marker); +} + +/*<asyxml><function type="void" signature="markangle(picture,Label,int,real,real,explicit line,explicit line,explicit vector,arrowbar,pen,filltype,margin,marker)"><code></asyxml>*/ +void markangle(picture pic = currentpicture, + Label L = "", int n = 1, real radius = 0, real space = 0, + explicit line l1, explicit line l2, explicit vector align, + arrowbar arrow = None, pen p = currentpen, + filltype filltype = NoFill, + margin margin = NoMargin, marker marker = nomarker) +{/*<asyxml></code><documentation>Mark the angle (l1, l2) in the direction 'dir' given relatively to 'l1'.</documentation></function></asyxml>*/ + markangle(pic, L, n, radius, space, l1, l2, (pair)align, arrow, + p, filltype, margin, marker); +} + +/*<asyxml><function type="void" signature="markangle(picture,Label,int,real,real,line,line,arrowbar,pen,filltype,margin,marker)"><code></asyxml>*/ +// void markangle(picture pic = currentpicture, +// Label L = "", int n = 1, real radius = 0, real space = 0, +// explicit line l1, explicit line l2, +// arrowbar arrow = None, pen p = currentpen, +// filltype filltype = NoFill, +// margin margin = NoMargin, marker marker = nomarker) +// {/*<asyxml></code><documentation>Mark the oriented angle (l1, l2).</documentation></function></asyxml>*/ +// if (parallel(l1, l2, true)) return; +// real al = degrees(l1, defaultcoordsys); +// pair O, A, B; +// if (radius == 0) radius = markangleradius(p); +// real d = degrees(locate(l1.u)); +// if (l1 == l2) { +// O = midpoint(segment(l1.A, l1.B)); +// } else { +// O = intersectionpoint(extend(l1), extend(l2)); +// } +// A = O + locate(l1.u); +// B = O + locate(l2.u); +// markangle(pic = pic, L = L, n = n, radius = radius, space = space, +// O = O, A = A, B = B, +// arrow = arrow, p = p, filltype = filltype, +// margin = margin, marker = marker); +// } + +/*<asyxml><function type="void" signature="perpendicularmark(picture,line,line,real,pen,int,margin,filltype)"><code></asyxml>*/ +void perpendicularmark(picture pic = currentpicture, line l1, line l2, + real size = 0, pen p = currentpen, int quarter = 1, + margin margin = NoMargin, filltype filltype = NoFill) +{/*<asyxml></code><documentation>Draw a right angle at the intersection point of lines and + aligned in the 'quarter' nth quarter of circle formed by 'l1.u' and + 'l2.u'.</documentation></function></asyxml>*/ + point P = intersectionpoint(l1, l2); + pair align = rotate(90 * (quarter - 1)) * dir(45); + perpendicularmark(P, align, locate(l1.u), size, p, margin, filltype); +} +// *.........................LINES.........................* +// *=======================================================* + +// *=======================================================* +// *........................CONICS.........................* +/*<asyxml><struct signature="bqe"><code></asyxml>*/ +struct bqe +{/*<asyxml></code><documentation>Bivariate Quadratic Equation.</documentation></asyxml>*/ + /*<asyxml><property type = "real[]" signature="a"><code></asyxml>*/ + real[] a;/*<asyxml></code><documentation>a[0] * x^2 + a[1] * x * y + a[2] * y^2 + a[3] * x + a[4] * y + a[5] = 0</documentation></property><property type = "coordsys" signature="coordsys"><code></asyxml>*/ + coordsys coordsys;/*<asyxml></code></property></asyxml>*/ +}/*<asyxml></struct></asyxml>*/ + +/*<asyxml><function type="bqe" signature="bqe(coordsys,real,real,real,real,real,real)"><code></asyxml>*/ +bqe bqe(coordsys R = currentcoordsys, + real a, real b, real c, real d, real e, real f) +{/*<asyxml></code><documentation>Return the bivariate quadratic equation + a[0] * x^2 + a[1] * x * y + a[2] * y^2 + a[3] * x + a[4] * y + a[5] = 0 + relatively to the coordinate system R.</documentation></function></asyxml>*/ + bqe obqe; + obqe.coordsys = R; + obqe.a = new real[] {a, b, c, d, e, f}; + return obqe; +} + +/*<asyxml><function type="bqe" signature="changecoordsys(coordsys,bqe)"><code></asyxml>*/ +bqe changecoordsys(coordsys R, bqe bqe) +{/*<asyxml></code><documentation>Returns the bivariate quadratic equation relatively to 'R'.</documentation></function></asyxml>*/ + pair i = coordinates(changecoordsys(R, vector(defaultcoordsys, + bqe.coordsys.i))); + pair j = coordinates(changecoordsys(R, vector(defaultcoordsys, + bqe.coordsys.j))); + pair O = coordinates(changecoordsys(R, point(defaultcoordsys, + bqe.coordsys.O))); + real a = bqe.a[0], b = bqe.a[1], c = bqe.a[2], d = bqe.a[3], f = bqe.a[4], g = bqe.a[5]; + real ux = i.x, uy = i.y; + real vx = j.x, vy = j.y; + real ox = O.x, oy = O.y; + real D = ux * vy - uy * vx; + real ap = (a * vy^2 - b * uy * vy + c * uy^2)/D^2; + real bpp = (-2 * a * vx * vy + b * ux * vy + b * uy * vx - 2 * c * ux * uy)/D^2; + real cp = (a * vx^2 - b * ux * vx + c * ux^2)/D^2; + real dp = (-2a * ox * vy^2 + 2a * oy * vx * vy + 2b * ox * uy * vy- + b * oy * ux * vy - b * oy * uy * vx - 2c * ox * uy^2 + 2c * oy * uy * ux)/D^2+ + (d * vy - f * uy)/D; + real fp = (2a * ox * vx * vy - b * ox * ux * vy - 2a * oy * vx^2- + b * ox * uy * vx + 2 * b * oy * ux * vx + 2c * ox * ux * uy - 2c * oy * ux^2)/D^2+ + (f * ux - d * vx)/D; + g = (a * ox^2 * vy^2 - 2a * ox * oy * vx * vy - b * ox^2 * uy * vy + b * ox * oy * ux * vy+ + a * oy^2 * vx^2 + b * ox * oy * uy * vx - b * oy^2 * ux * vx + c * ox^2 * uy^2- + 2 * c * ox * oy * ux * uy + c * oy^2 * ux^2)/D^2+ + (d * oy * vx + f * ox * uy - d * ox * vy - f * oy * ux)/D + g; + bqe obqe; + obqe.a = approximate(new real[] {ap, bpp, cp, dp, fp, g}); + obqe.coordsys = R; + return obqe; +} + +/*<asyxml><function type="bqe" signature="bqe(point,point,point,point,point)"><code></asyxml>*/ +bqe bqe(point M1, point M2, point M3, point M4, point M5) +{/*<asyxml></code><documentation>Return the bqe of conic passing through the five points (if possible).</documentation></function></asyxml>*/ + coordsys R; + pair[] pts; + if (samecoordsys(M1, M2, M3, M4, M5)) { + R = M1.coordsys; + pts= new pair[] {M1.coordinates, M2.coordinates, M3.coordinates, M4.coordinates, M5.coordinates}; + } else { + R = defaultcoordsys; + pts= new pair[] {M1, M2, M3, M4, M5}; + } + real[][] M; + real[] x; + bqe bqe; + bqe.coordsys = R; + for (int i = 0; i < 5; ++i) {// Try a = -1 + M[i] = new real[] {pts[i].x * pts[i].y, pts[i].y^2, pts[i].x, pts[i].y, 1}; + x[i] = pts[i].x^2; + } + if(abs(determinant(M)) < 1e-5) {// Try c = -1 + for (int i = 0; i < 5; ++i) { + M[i] = new real[] {pts[i].x^2, pts[i].x * pts[i].y, pts[i].x, pts[i].y, 1}; + x[i] = pts[i].y^2; + } + real[] coef = solve(M, x); + bqe.a = new real[] {coef[0], coef[1], -1, coef[2], coef[3], coef[4]}; + } else { + real[] coef = solve(M, x); + bqe.a = new real[] {-1, coef[0], coef[1], coef[2], coef[3], coef[4]}; + } + bqe.a = approximate(bqe.a); + return bqe; +} + +/*<asyxml><function type="bool" signature="samecoordsys(bool...bqe[])"><code></asyxml>*/ +bool samecoordsys(bool warn = true ... bqe[] bqes) +{/*<asyxml></code><documentation>Return true if all the bivariate quadratic equations have the same coordinate system.</documentation></function></asyxml>*/ + bool ret = true; + coordsys t = bqes[0].coordsys; + for (int i = 1; i < bqes.length; ++i) { + ret = (t == bqes[i].coordsys); + if(!ret) break; + t = bqes[i].coordsys; + } + if(warn && !ret) + warning("coodinatesystem", + "the coordinate system of two bivariate quadratic equations are not +the same. The operation will be done relatively to the default coordinate +system."); + return ret; +} + +/*<asyxml><function type="real[]" signature="realquarticroots(real,real,real,real,real)"><code></asyxml>*/ +real[] realquarticroots(real a, real b, real c, real d, real e) +{/*<asyxml></code><documentation>Return the real roots of the quartic equation ax^4 + b^x3 + cx^2 + dx = 0.</documentation></function></asyxml>*/ + static real Fuzz = sqrt(realEpsilon); + pair[] zroots = quarticroots(a, b, c, d, e); + real[] roots; + real p(real x){return a * x^4 + b * x^3 + c * x^2 + d * x + e;} + real prime(real x){return 4 * a * x^3 + 3 * b * x^2 + 2 * c * x + d;} + real x; + bool search = true; + int n; + void addroot(real x) + { + bool exist = false; + for (int i = 0; i < roots.length; ++i) { + if(abs(roots[i]-x) < 1e-5) {exist = true; break;} + } + if(!exist) roots.push(x); + } + for(int i = 0; i < zroots.length; ++i) { + if(zroots[i].y == 0 || abs(p(zroots[i].x)) < Fuzz) addroot(zroots[i].x); + else { + if(abs(zroots[i].y) < 1e-3) { + x = zroots[i].x; + search = true; + n = 200; + while(search) { + real tx = abs(p(x)) < Fuzz ? x : newton(iterations = n, p, prime, x); + if(tx < realMax) { + if(abs(p(tx)) < Fuzz) { + addroot(tx); + search = false; + } else if(n < 200) n *=2; + else { + search = false; + } + } else search = false; //It's not a real root. + } + } + } + } + return roots; +} + +/*<asyxml><function type="point[]" signature="intersectionpoints(bqe,bqe)"><code></asyxml>*/ +point[] intersectionpoints(bqe bqe1, bqe bqe2) +{/*<asyxml></code><documentation>Return the interscetion of the two conic sections whose equations are 'bqe1' and 'bqe2'.</documentation></function></asyxml>*/ + coordsys R = bqe1.coordsys; + bqe lbqe1, lbqe2; + real[] a, b; + if(R != bqe2.coordsys) { + R = currentcoordsys; + a = changecoordsys(R, bqe1).a; + b = changecoordsys(R, bqe2).a; + } else { + a = bqe1.a; + b = bqe2.a; + } + static real e = 100 * sqrt(realEpsilon); + real[] x, y, c; + point[] P; + if(abs(a[0]-b[0]) > e || abs(a[1]-b[1]) > e || abs(a[2]-b[2]) > e) { + c = new real[] {-2 * a[0]*a[2]*b[0]*b[2]+a[0]*a[2]*b[1]^2 - a[0]*a[1]*b[2]*b[1]+a[1]^2 * b[0]*b[2]- + a[2]*a[1]*b[0]*b[1]+a[0]^2 * b[2]^2 + a[2]^2 * b[0]^2, + -a[2]*a[1]*b[0]*b[4]-a[2]*a[4]*b[0]*b[1]-a[1]*a[3]*b[2]*b[1]+2 * a[0]*a[2]*b[1]*b[4]- + a[0]*a[1]*b[2]*b[4]+a[1]^2 * b[2]*b[3]-2 * a[2]*a[3]*b[0]*b[2]-2 * a[0]*a[2]*b[2]*b[3]+ + a[2]*a[3]*b[1]^2 - a[2]*a[1]*b[1]*b[3]+2 * a[1]*a[4]*b[0]*b[2]+2 * a[2]^2 * b[0]*b[3]- + a[0]*a[4]*b[2]*b[1]+2 * a[0]*a[3]*b[2]^2, + -a[3]*a[4]*b[2]*b[1]+a[2]*a[5]*b[1]^2 - a[1]*a[5]*b[2]*b[1]-a[1]*a[3]*b[2]*b[4]+ + a[1]^2 * b[2]*b[5]-2 * a[2]*a[3]*b[2]*b[3]+2 * a[2]^2 * b[0]*b[5]+2 * a[0]*a[5]*b[2]^2 + a[3]^2 * b[2]^2- + 2 * a[2]*a[5]*b[0]*b[2]+2 * a[1]*a[4]*b[2]*b[3]-a[2]*a[4]*b[1]*b[3]-2 * a[0]*a[2]*b[2]*b[5]+ + a[2]^2 * b[3]^2 + 2 * a[2]*a[3]*b[1]*b[4]-a[2]*a[4]*b[0]*b[4]+a[4]^2 * b[0]*b[2]-a[2]*a[1]*b[3]*b[4]- + a[2]*a[1]*b[1]*b[5]-a[0]*a[4]*b[2]*b[4]+a[0]*a[2]*b[4]^2, + -a[4]*a[5]*b[2]*b[1]+a[2]*a[3]*b[4]^2 + 2 * a[3]*a[5]*b[2]^2 - a[2]*a[1]*b[4]*b[5]- + a[2]*a[4]*b[3]*b[4]+2 * a[2]^2 * b[3]*b[5]-2 * a[2]*a[3]*b[2]*b[5]-a[3]*a[4]*b[2]*b[4]- + 2 * a[2]*a[5]*b[2]*b[3]-a[2]*a[4]*b[1]*b[5]+2 * a[1]*a[4]*b[2]*b[5]-a[1]*a[5]*b[2]*b[4]+ + a[4]^2 * b[2]*b[3]+2 * a[2]*a[5]*b[1]*b[4], + -2 * a[2]*a[5]*b[2]*b[5]+a[4]^2 * b[2]*b[5]+a[5]^2 * b[2]^2 - a[4]*a[5]*b[2]*b[4]+a[2]*a[5]*b[4]^2+ + a[2]^2 * b[5]^2 - a[2]*a[4]*b[4]*b[5]}; + x = realquarticroots(c[0], c[1], c[2], c[3], c[4]); + } else { + if(abs(b[4]-a[4]) > e){ + real D = (b[4]-a[4])^2; + c = new real[] {(a[0]*b[4]^2 + (-a[1]*b[3]-2 * a[0]*a[4]+a[1]*a[3]) * b[4]+a[2]*b[3]^2+ + (a[1]*a[4]-2 * a[2]*a[3]) * b[3]+a[0]*a[4]^2 - a[1]*a[3]*a[4]+a[2]*a[3]^2)/D, + -((a[1]*b[4]-2 * a[2]*b[3]-a[1]*a[4]+2 * a[2]*a[3]) * b[5]-a[3]*b[4]^2 + (a[4]*b[3]-a[1]*a[5]+a[3]*a[4]) * b[4]+(2 * a[2]*a[5]-a[4]^2) * b[3]+(a[1]*a[4]-2 * a[2]*a[3]) * a[5])/D, + a[2]*(a[5]-b[5])^2/D + a[4]*(a[5]-b[5])/(b[4]-a[4]) + a[5]}; + x = quadraticroots(c[0], c[1], c[2]); + } else { + if(abs(a[3]-b[3]) > e) { + real D = b[3]-a[3]; + c = new real[] {a[2], (-a[1]*b[5] + a[4]*b[3] + a[1]*a[5] - a[3]*a[4])/D, + a[0]*(a[5]-b[5])^2/D^2 + a[3]*(a[5]-b[5])/D + a[5]}; + y = quadraticroots(c[0], c[1], c[2]); + for (int i = 0; i < y.length; ++i) { + c = new real[] {a[0], a[1]*y[i]+a[3], a[2]*y[i]^2 + a[4]*y[i]+a[5]}; + x = quadraticroots(c[0], c[1], c[2]); + for (int j = 0; j < x.length; ++j) { + if(abs(b[0]*x[j]^2 + b[1]*x[j]*y[i]+b[2]*y[i]^2 + b[3]*x[j]+b[4]*y[i]+b[5]) < 1e-5) + P.push(point(R, (x[j], y[i]))); + } + } + return P; + } else { + if(abs(a[5]-b[5]) < e) abort("intersectionpoints: intersection of identical conics."); + } + } + } + for (int i = 0; i < x.length; ++i) { + c = new real[] {a[2], a[1]*x[i]+a[4], a[0]*x[i]^2 + a[3]*x[i]+a[5]}; + y = quadraticroots(c[0], c[1], c[2]); + for (int j = 0; j < y.length; ++j) { + if(abs(b[0]*x[i]^2 + b[1]*x[i]*y[j]+b[2]*y[j]^2 + b[3]*x[i]+b[4]*y[j]+b[5]) < 1e-5) + P.push(point(R, (x[i], y[j]))); + } + } + return P; +} + +/*<asyxml><struct signature="conic"><code></asyxml>*/ +struct conic +{/*<asyxml></code><documentation></documentation><property type = "real" signature="e,p,h"><code></asyxml>*/ + real e, p, h;/*<asyxml></code><documentation>BE CAREFUL: h = distance(F, D) and p = h * e (http://en.wikipedia.org/wiki/Ellipse) + While http://mathworld.wolfram.com/ takes p = distance(F,D).</documentation></property><property type = "point" signature="F"><code></asyxml>*/ + point F;/*<asyxml></code><documentation>Focus.</documentation></property><property type = "line" signature="D"><code></asyxml>*/ + line D;/*<asyxml></code><documentation>Directrix.</documentation></property><property type = "line" signature="l"><code></asyxml>*/ + line[] l;/*<asyxml></code><documentation>Case of degenerated conic (not yet implemented !).</documentation></property></asyxml>*/ +}/*<asyxml></struct></asyxml>*/ + +bool degenerate(conic c) +{ + return !finite(c.p) || !finite(c.h); +} + +/*ANCconic conic(point, line, real)ANC*/ +conic conic(point F, line l, real e) +{/*DOC + The conic section define by the eccentricity 'e', the focus 'F' + and the directrix 'l'. + Note that an eccentricity equal to 0 defines a circle centered at F, + with a radius equal at the distance from 'F' to 'l'. + If the coordinate system of 'F' and 'l' are not identical, the conic is + attached to 'defaultcoordsys'. + DOC*/ + if(e < 0) abort("conic: 'e' can't be negative."); + conic oc; + point[] P = standardizecoordsys(F, l.A, l.B); + line ll; + ll = line(P[1], P[2]); + oc.e = e < epsgeo ? 0 : e; // Handle case of circle. + oc.F = P[0]; + oc.D = ll; + oc.h = distance(P[0], ll); + oc.p = abs(e) < epsgeo ? oc.h : e * oc.h; + return oc; +} + +/*<asyxml><struct signature="circle"><code></asyxml>*/ +struct circle +{/*<asyxml></code><documentation>All the calculus with this structure will be as exact as Asymptote can do. + For a full precision, you must not cast 'circle' to 'path' excepted for drawing routines.</documentation></asyxml>*/ + /*<asyxml><property type = "point" signature="C"><code></asyxml>*/ + point C;/*<asyxml></code><documentation>Center</documentation></property><property><code></asyxml>*/ + real r;/*<asyxml></code><documentation>Radius</documentation></property><property><code></asyxml>*/ + line l;/*<asyxml></code><documentation>If the radius is infinite, this line is used instead of circle.</documentation></property></asyxml>*/ +}/*<asyxml></struct></asyxml>*/ + +bool degenerate(circle c) +{ + return !finite(c.r); +} + +line line(circle c){ + if(finite(c.r)) abort("Circle can not be casted to line here."); + return c.l; +} + +/*<asyxml><struct signature="ellipse"><code></asyxml>*/ +struct ellipse +{/*<asyxml></code><documentation>Look at <html><a href = "http://mathworld.wolfram.com/Ellipse.html">http://mathworld.wolfram.com/Ellipse.html</a></html></documentation></asyxml>*/ + /*<asyxml><property type = "point" signature="F1,F2,C"><code></asyxml>*/ + restricted point F1,F2,C;/*<asyxml></code><documentation>Foci and center.</documentation></property><property type = "real" signature="a,b,c,e,p"><code></asyxml>*/ + restricted real a,b,c,e,p;/*<asyxml></code></property><property type = "real" signature="angle"><code></asyxml>*/ + restricted real angle;/*<asyxml></code><documentation>Value is degrees(F1 - F2).</documentation></property><property type = "line" signature="D1,D2"><code></asyxml>*/ + restricted line D1,D2;/*<asyxml></code><documentation>Directrices.</documentation></property><property type = "line" signature="l"><code></asyxml>*/ + line l;/*<asyxml></code><documentation>If one axis is infinite, this line is used instead of ellipse.</documentation></property></asyxml>*/ + /*<asyxml><method type = "void" signature="init(point,point,real)"><code></asyxml>*/ + void init(point f1, point f2, real a) + {/*<asyxml></code><documentation>Ellipse given by foci and semimajor axis</documentation></method></asyxml>*/ + point[] P = standardizecoordsys(f1, f2); + this.F1 = P[0]; + this.F2 = P[1]; + this.angle = abs(P[1]-P[0]) < 10 * epsgeo ? 0 : degrees(P[1]-P[0]); + this.C = (P[0] + P[1])/2; + this.a = a; + if(!finite(a)) { + this.l = line(P[0], P[1]); + this.b = infinity; + this.e = 0; + this.c = 0; + } else { + this.c = abs(C - P[0]); + this.b = this.c < epsgeo ? a : sqrt(a^2 - c^2); // Handle case of circle. + this.e = this.c < epsgeo ? 0 : this.c/a; // Handle case of circle. + if(this.e >= 1) abort("ellipse.init: wrong parameter: e >= 1."); + this.p = a * (1 - this.e^2); + if (this.c != 0) {// directrix is not set for a circle. + point A = this.C + (a^2/this.c) * unit(P[0]-this.C); + this.D1 = line(A, A + rotateO(90) * unit(A - this.C)); + this.D2 = reverse(rotate(180, C) * D1); + } + } + } +}/*<asyxml></struct></asyxml>*/ + +bool degenerate(ellipse el) +{ + return (!finite(el.a) || !finite(el.b)); +} + +/*<asyxml><struct signature="parabola"><code></asyxml>*/ +struct parabola +{/*<asyxml></code><documentation>Look at <html><a href = "http://mathworld.wolfram.com/Parabola.html">http://mathworld.wolfram.com/Parabola.html</a></html></documentation><property type = "point" signature="F,V"><code></asyxml>*/ + restricted point F,V;/*<asyxml></code><documentation>Focus and vertex</documentation></property><property type = "real" signature="a,p,e = 1"><code></asyxml>*/ + restricted real a,p,e = 1;/*<asyxml></code></property><property type = "real" signature="angle"><code></asyxml>*/ + restricted real angle;/*<asyxml></code><documentation>Angle, in degrees, of the line (FV).</documentation></property><property type = "line" signature="D"><code></asyxml>*/ + restricted line D;/*<asyxml></code><documentation>Directrix</documentation></property><property type = "pair" signature="bmin,bmax"><code></asyxml>*/ + pair bmin, bmax;/*<asyxml></code><documentation>The (left, bottom) and (right, top) coordinates of region bounding box for drawing the parabola. + If unset the current picture bounding box is used instead.</documentation></property></asyxml>*/ + + /*<asyxml><method type = "void" signature="init(point,line)"><code></asyxml>*/ + void init(point F, line directrix) + {/*<asyxml></code><documentation>Parabola given by focus and directrix.</documentation></method></asyxml>*/ + point[] P = standardizecoordsys(F, directrix.A, directrix.B); + line l = line(P[1], P[2]); + this.F = P[0]; + this.D = l; + this.a = distance(P[0], l)/2; + this.p = 2 * a; + this.V = 0.5 * (F + projection(D) * P[0]); + this.angle = degrees(F - V); + } +}/*<asyxml></struct></asyxml>*/ + +/*<asyxml><struct signature="hyperbola"><code></asyxml>*/ +struct hyperbola +{/*<asyxml></code><documentation><html>Look at <a href = "http://mathworld.wolfram.com/Hyperbola.html">http://mathworld.wolfram.com/Hyperbola.html</a></html></documentation><property type = "point" signature="F1,F2"><code></asyxml>*/ + restricted point F1,F2;/*<asyxml></code><documentation>Foci.</documentation></property><property type = "point" signature="C,V1,V2"><code></asyxml>*/ + restricted point C,V1,V2;/*<asyxml></code><documentation>Center and vertices.</documentation></property><property type = "real" signature="a,b,c,e,p"><code></asyxml>*/ + restricted real a,b,c,e,p;/*<asyxml></code><documentation></documentation></property><property type = "real" signature="angle"><code></asyxml>*/ + restricted real angle;/*<asyxml></code><documentation>Angle,in degrees,of the line (F1F2).</documentation></property><property type = "line" signature="D1,D2,A1,A2"><code></asyxml>*/ + restricted line D1,D2,A1,A2;/*<asyxml></code><documentation>Directrices and asymptotes.</documentation></property><property type = "pair" signature="bmin,bmax"><code></asyxml>*/ + pair bmin, bmax; /*<asyxml></code><documentation>The (left, bottom) and (right, top) coordinates of region bounding box for drawing the hyperbola. + If unset the current picture bounding box is used instead.</documentation></property></asyxml>*/ + + /*<asyxml><method type = "void" signature="init(point,point,real)"><code></asyxml>*/ + void init(point f1, point f2, real a) + {/*<asyxml></code><documentation>Hyperbola given by foci and semimajor axis.</documentation></method></asyxml>*/ + point[] P = standardizecoordsys(f1, f2); + this.F1 = P[0]; + this.F2 = P[1]; + this.angle = degrees(F2 - F1); + this.a = a; + this.C = (P[0] + P[1])/2; + this.c = abs(C - P[0]); + this.e = this.c/a; + if(this.e <= 1) abort("hyperbola.init: wrong parameter: e <= 1."); + this.b = a * sqrt(this.e^2 - 1); + this.p = a * (this.e^2 - 1); + point A = this.C + (a^2/this.c) * unit(P[0]-this.C); + this.D1 = line(A, A + rotateO(90) * unit(A - this.C)); + this.D2 = reverse(rotate(180, C) * D1); + this.V1 = C + a * unit(F1 - C); + this.V2 = C + a * unit(F2 - C); + this.A1 = line(C, V1 + b * unit(rotateO(-90) * (C - V1))); + this.A2 = line(C, V1 + b * unit(rotateO(90) * (C - V1))); + } +}/*<asyxml></struct></asyxml>*/ + +/*<asyxml><variable type="int" signature="conicnodesfactor"><code></asyxml>*/ +int conicnodesfactor = 1;/*<asyxml></code><documentation>Factor for the node number of all conics.</documentation></variable></asyxml>*/ + +/*<asyxml><variable type="int" signature="circlenodesnumberfactor"><code></asyxml>*/ +int circlenodesnumberfactor = 100;/*<asyxml></code><documentation>Factor for the node number of circles.</documentation></variable></asyxml>*/ +/*<asyxml><function type="int" signature="circlenodesnumber(real)"><code></asyxml>*/ +int circlenodesnumber(real r) +{/*<asyxml></code><documentation>Return the number of nodes for drawing a circle of radius 'r'.</documentation></function></asyxml>*/ + if (circlenodesnumberfactor < 100) + warning("circlenodesnumberfactor", + "variable 'circlenodesnumberfactor' may be too small."); + int oi = ceil(circlenodesnumberfactor * abs(r)^0.1); + oi = 45 * floor(oi/45); + return oi == 0 ? 4 : conicnodesfactor * oi; +} + +/*<asyxml><function type="int" signature="circlenodesnumber(real,real,real)"><code></asyxml>*/ +int circlenodesnumber(real r, real angle1, real angle2) +{/*<asyxml></code><documentation>Return the number of nodes to draw a circle arc.</documentation></function></asyxml>*/ + return (r > 0) ? + ceil(circlenodesnumber(r) * abs(angle1 - angle2)/360) : + ceil(circlenodesnumber(r) * abs((1 - abs(angle1 - angle2)/360))); +} + +/*<asyxml><variable type="int" signature="ellispenodesnumberfactor"><code></asyxml>*/ +int ellipsenodesnumberfactor = 250;/*<asyxml></code><documentation>Factor for the node number of ellispe (non-circle).</documentation></variable></asyxml>*/ +/*<asyxml><function type="int" signature="ellipsenodesnumber(real,real)"><code></asyxml>*/ +int ellipsenodesnumber(real a, real b) +{/*<asyxml></code><documentation>Return the number of nodes to draw a ellipse of axis 'a' and 'b'.</documentation></function></asyxml>*/ + if (ellipsenodesnumberfactor < 250) + write("ellipsenodesnumberfactor", + "variable 'ellipsenodesnumberfactor' maybe too small."); + int tmp = circlenodesnumberfactor; + circlenodesnumberfactor = ellipsenodesnumberfactor; + int oi = circlenodesnumber(max(abs(a), abs(b))/min(abs(a), abs(b))); + circlenodesnumberfactor = tmp; + return conicnodesfactor * oi; +} + +/*<asyxml><function type="int" signature="ellipsenodesnumber(real,real,real)"><code></asyxml>*/ +int ellipsenodesnumber(real a, real b, real angle1, real angle2, bool dir) +{/*<asyxml></code><documentation>Return the number of nodes to draw an ellipse arc.</documentation></function></asyxml>*/ + real d; + real da = angle2 - angle1; + if(dir) { + d = angle1 < angle2 ? da : 360 + da; + } else { + d = angle1 < angle2 ? -360 + da : da; + } + int n = floor(ellipsenodesnumber(a, b) * abs(d)/360); + return n < 5 ? 5 : n; +} + +/*<asyxml><variable type="int" signature="parabolanodesnumberfactor"><code></asyxml>*/ +int parabolanodesnumberfactor = 100;/*<asyxml></code><documentation>Factor for the number of nodes of parabolas.</documentation></variable></asyxml>*/ +/*<asyxml><function type="int" signature="parabolanodesnumber(parabola,real,real)"><code></asyxml>*/ +int parabolanodesnumber(parabola p, real angle1, real angle2) +{/*<asyxml></code><documentation>Return the number of nodes for drawing a parabola.</documentation></function></asyxml>*/ + return conicnodesfactor * floor(0.01 * parabolanodesnumberfactor * abs(angle1 - angle2)); +} + +/*<asyxml><variable type="int" signature="hyperbolanodesnumberfactor"><code></asyxml>*/ +int hyperbolanodesnumberfactor = 100;/*<asyxml></code><documentation>Factor for the number of nodes of hyperbolas.</documentation></variable></asyxml>*/ +/*<asyxml><function type="int" signature="hyperbolanodesnumber(hyperbola,real,real)"><code></asyxml>*/ +int hyperbolanodesnumber(hyperbola h, real angle1, real angle2) +{/*<asyxml></code><documentation>Return the number of nodes for drawing an hyperbola.</documentation></function></asyxml>*/ + return conicnodesfactor * floor(0.01 * hyperbolanodesnumberfactor * abs(angle1 - angle2)/h.e); +} + +/*<asyxml><operator type = "conic" signature="+(conic,explicit point)"><code></asyxml>*/ +conic operator +(conic c, explicit point M) +{/*<asyxml></code><documentation></documentation></operator></asyxml>*/ + return conic(c.F + M, c.D + M, c.e); +} +/*<asyxml><operator type = "conic" signature="-(conic,explicit point)"><code></asyxml>*/ +conic operator -(conic c, explicit point M) +{/*<asyxml></code><documentation></documentation></operator></asyxml>*/ + return conic(c.F - M, c.D - M, c.e); +} +/*<asyxml><operator type = "conic" signature="+(conic,explicit pair)"><code></asyxml>*/ +conic operator +(conic c, explicit pair m) +{/*<asyxml></code><documentation></documentation></operator></asyxml>*/ + point M = point(c.F.coordsys, m); + return conic(c.F + M, c.D + M, c.e); +} +/*<asyxml><operator type = "conic" signature="-(conic,explicit pair)"><code></asyxml>*/ +conic operator -(conic c, explicit pair m) +{/*<asyxml></code><documentation></documentation></operator></asyxml>*/ + point M = point(c.F.coordsys, m); + return conic(c.F - M, c.D - M, c.e); +} +/*<asyxml><operator type = "conic" signature="+(conic,vector)"><code></asyxml>*/ +conic operator +(conic c, vector v) +{/*<asyxml></code><documentation></documentation></operator></asyxml>*/ + return conic(c.F + v, c.D + v, c.e); +} +/*<asyxml><operator type = "conic" signature="-(conic,vector)"><code></asyxml>*/ +conic operator -(conic c, vector v) +{/*<asyxml></code><documentation></documentation></operator></asyxml>*/ + return conic(c.F - v, c.D - v, c.e); +} + +/*<asyxml><function type="coordsys" signature="coordsys(conic)"><code></asyxml>*/ +coordsys coordsys(conic co) +{/*<asyxml></code><documentation>Return the coordinate system of 'co'.</documentation></function></asyxml>*/ + return co.F.coordsys; +} + +/*<asyxml><function type="conic" signature="changecoordsys(coordsys,conic)"><code></asyxml>*/ +conic changecoordsys(coordsys R, conic co) +{/*<asyxml></code><documentation>Change the coordinate system of 'co' to 'R'</documentation></function></asyxml>*/ + line l = changecoordsys(R, co.D); + point F = changecoordsys(R, co.F); + return conic(F, l, co.e); +} + +/*<asyxml><typedef type = "polarconicroutine" return = "path" params = "conic, real, real, int, bool"><code></asyxml>*/ +typedef path polarconicroutine(conic co, real angle1, real angle2, int n, bool direction);/*<asyxml></code><documentation>Routine type used to draw conics from 'angle1' to 'angle2'</documentation></typedef></asyxml>*/ + +/*<asyxml><function type="path" signature="arcfromfocus(conic,real,real,int,bool)"><code></asyxml>*/ +path arcfromfocus(conic co, real angle1, real angle2, int n = 400, bool direction = CCW) +{/*<asyxml></code><documentation>Return the path of the conic section 'co' from angle1 to angle2 in degrees, + drawing in the given direction, with n nodes.</documentation></function></asyxml>*/ + guide op; + if (n < 1) return op; + if (angle1 > angle2) { + path g = arcfromfocus(co, angle2, angle1, n, !direction); + return g == nullpath ? g : reverse(g); + } + point O = projection(co.D) * co.F; + pair i = unit(locate(co.F) - locate(O)); + pair j = rotate(90) * i; + coordsys Rp = cartesiansystem(co.F, i, j); + real a1 = direction ? radians(angle1) : radians(angle2); + real a2 = direction ? radians(angle2) : radians(angle1) + 2 * pi; + real step = n == 1 ? 0 : (a2 - a1)/(n - 1); + real a, r; + for (int i = 0; i < n; ++i) { + a = a1 + i * step; + if(co.e >= 1) { + r = 1 - co.e * cos(a); + if(r > epsgeo) { + r = co.p/r; + op = op--Rp * Rp.polar(r, a); + } + } else { + r = co.p/(1 - co.e * cos(a)); + op = op..Rp * Rp.polar(r, a); + } + } + if(co.e < 1 && abs(abs(a2 - a1) - 2 * pi) < epsgeo) op = (path)op..cycle; + + return (direction ? op : op == nullpath ? op :reverse(op)); +} + +/*<asyxml><variable type="polarconicroutine" signature="currentpolarconicroutine"><code></asyxml>*/ +polarconicroutine currentpolarconicroutine = arcfromfocus;/*<asyxml></code><documentation>Default routine used to cast conic section to path.</documentation></variable></asyxml>*/ + +/*<asyxml><function type="point" signature="angpoint(conic,real)"><code></asyxml>*/ +point angpoint(conic co, real angle) +{/*<asyxml></code><documentation>Return the point of 'co' whose the angular (in degrees) + coordinate is 'angle' (mesured from the focus of 'co', relatively + to its 'natural coordinate system').</documentation></function></asyxml>*/ + coordsys R = coordsys(co); + return point(R, point(arcfromfocus(co, angle, angle, 1, CCW), 0)/R); +} + +/*<asyxml><operator type = "bool" signature="@(point,conic)"><code></asyxml>*/ +bool operator @(point M, conic co) +{/*<asyxml></code><documentation>Return true iff 'M' on 'co'.</documentation></operator></asyxml>*/ + if(co.e == 0) return abs(abs(co.F - M) - co.p) < 10 * epsgeo; + return abs(co.e * distance(M, co.D) - abs(co.F - M)) < 10 * epsgeo; +} + +/*<asyxml><function type="coordsys" signature="coordsys(ellipse)"><code></asyxml>*/ +coordsys coordsys(ellipse el) +{/*<asyxml></code><documentation>Return the coordinate system of 'el'.</documentation></function></asyxml>*/ + return el.F1.coordsys; +} + +/*<asyxml><function type="coordsys" signature="canonicalcartesiansystem(ellipse)"><code></asyxml>*/ +coordsys canonicalcartesiansystem(ellipse el) +{/*<asyxml></code><documentation>Return the canonical cartesian system of the ellipse 'el'.</documentation></function></asyxml>*/ + if(degenerate(el)) return cartesiansystem(el.l.A, el.l.u, el.l.v); + pair O = locate(el.C); + pair i = el.e == 0 ? el.C.coordsys.i : unit(locate(el.F1) - O); + pair j = rotate(90) * i; + return cartesiansystem(O, i, j); +} + +/*<asyxml><function type="coordsys" signature="canonicalcartesiansystem(parabola)"><code></asyxml>*/ +coordsys canonicalcartesiansystem(parabola p) +{/*<asyxml></code><documentation>Return the canonical cartesian system of a parabola, + so that Origin = vertex of 'p' and directrix: x = -a.</documentation></function></asyxml>*/ + point A = projection(p.D) * p.F; + pair O = locate((A + p.F)/2); + pair i = unit(locate(p.F) - O); + pair j = rotate(90) * i; + return cartesiansystem(O, i, j); +} + +/*<asyxml><function type="coordsys" signature="canonicalcartesiansystem(hyperbola)"><code></asyxml>*/ +coordsys canonicalcartesiansystem(hyperbola h) +{/*<asyxml></code><documentation>Return the canonical cartesian system of an hyperbola.</documentation></function></asyxml>*/ + pair O = locate(h.C); + pair i = unit(locate(h.F2) - O); + pair j = rotate(90) * i; + return cartesiansystem(O, i, j); +} + +/*<asyxml><function type="ellipse" signature="ellipse(point,point,real)"><code></asyxml>*/ +ellipse ellipse(point F1, point F2, real a) +{/*<asyxml></code><documentation>Return the ellipse whose the foci are 'F1' and 'F2' + and the semimajor axis is 'a'.</documentation></function></asyxml>*/ + ellipse oe; + oe.init(F1, F2, a); + return oe; +} + +/*<asyxml><constant type = "bool" signature="byfoci,byvertices"><code></asyxml>*/ +restricted bool byfoci = true, byvertices = false;/*<asyxml></code><documentation>Constants useful for the routine 'hyperbola(point P1, point P2, real ae, bool byfoci = byfoci)'</documentation></constant></asyxml>*/ + +/*<asyxml><function type="hyperbola" signature="hyperbola(point,point,real,bool)"><code></asyxml>*/ +hyperbola hyperbola(point P1, point P2, real ae, bool byfoci = byfoci) +{/*<asyxml></code><documentation>if 'byfoci = true': + return the hyperbola whose the foci are 'P1' and 'P2' + and the semimajor axis is 'ae'. + else return the hyperbola whose vertexes are 'P1' and 'P2' with eccentricity 'ae'.</documentation></function></asyxml>*/ + hyperbola oh; + point[] P = standardizecoordsys(P1, P2); + if(byfoci) { + oh.init(P[0], P[1], ae); + } else { + real a = abs(P[0]-P[1])/2; + vector V = unit(P[0]-P[1]); + point F1 = P[0] + a * (ae - 1) * V; + point F2 = P[1]-a * (ae - 1) * V; + oh.init(F1, F2, a); + } + return oh; +} + +/*<asyxml><function type="ellipse" signature="ellipse(point,point,point)"><code></asyxml>*/ +ellipse ellipse(point F1, point F2, point M) +{/*<asyxml></code><documentation>Return the ellipse passing through 'M' whose the foci are 'F1' and 'F2'.</documentation></function></asyxml>*/ + point P[] = standardizecoordsys(false, F1, F2, M); + real a = abs(F1 - M) + abs(F2 - M); + return ellipse(F1, F2, finite(a) ? a/2 : a); +} + +/*<asyxml><function type="ellipse" signature="ellipse(point,real,real,real)"><code></asyxml>*/ +ellipse ellipse(point C, real a, real b, real angle = 0) +{/*<asyxml></code><documentation>Return the ellipse centered at 'C' with semimajor axis 'a' along C--C + dir(angle), + semiminor axis 'b' along the perpendicular.</documentation></function></asyxml>*/ + ellipse oe; + coordsys R = C.coordsys; + angle += degrees(R.i); + if(a < b) {angle += 90; real tmp = a; a = b; b = tmp;} + if(finite(a) && finite(b)) { + real c = sqrt(abs(a^2 - b^2)); + point f1, f2; + if(abs(a - b) < epsgeo) { + f1 = C; f2 = C; + } else { + f1 = point(R, (locate(C) + rotate(angle) * (-c, 0))/R); + f2 = point(R, (locate(C) + rotate(angle) * (c, 0))/R); + } + oe.init(f1, f2, a); + } else { + if(finite(b) || !finite(a)) oe.init(C, C + R.polar(1, angle), infinity); + else oe.init(C, C + R.polar(1, 90 + angle), infinity); + } + return oe; +} + +/*<asyxml><function type="ellipse" signature="ellipse(bqe)"><code></asyxml>*/ +ellipse ellipse(bqe bqe) +{/*<asyxml></code><documentation>Return the ellipse a[0] * x^2 + a[1] * xy + a[2] * y^2 + a[3] * x + a[4] * y + a[5] = 0 + given in the coordinate system of 'bqe' with a[i] = bque.a[i]. + <url href = "http://mathworld.wolfram.com/QuadraticCurve.html"/> + <url href = "http://mathworld.wolfram.com/Ellipse.html"/>.</documentation></function></asyxml>*/ + bqe lbqe = changecoordsys(defaultcoordsys, bqe); + real a = lbqe.a[0], b = lbqe.a[1]/2, c = lbqe.a[2], d = lbqe.a[3]/2, f = lbqe.a[4]/2, g = lbqe.a[5]; + coordsys R = bqe.coordsys; + string message = "ellipse: the given equation is not an equation of an ellipse."; + real u = b^2 * g + d^2 * c + f^2 * a; + real delta = a * c * g + b * f * d + d * b * f - u; + if(abs(delta) < epsgeo) abort(message); + real j = b^2 - a * c; + real i = a + c; + real dd = j * (sgnd(c - a) * sqrt((a - c)^2 + 4 * (b^2)) - c-a); + real ddd = j * (-sgnd(c - a) * sqrt((a - c)^2 + 4 * (b^2)) - c-a); + + if(abs(ddd) < epsgeo || abs(dd) < epsgeo || + j >= -epsgeo || delta/sgnd(i) > 0) abort(message); + + real x = (c * d - b * f)/j, y = (a * f - b * d)/j; + // real dir = abs(b) < epsgeo ? 0 : pi/2-0.5 * acot(0.5 * (c-a)/b); + real dir = abs(b) < epsgeo ? 0 : 0.5 * acot(0.5 * (c - a)/b); + if(dir * (c - a) * b < 0) dir = dir < 0 ? dir + pi/2 : dir - pi/2; + real cd = cos(dir), sd = sin(dir); + real t = a * cd^2 - 2 * b * cd * sd + c * sd^2; + real tt = a * sd^2 + 2 * b * cd * sd + c * cd^2; + real gg = -g + ((d * cd - f * sd)^2)/t + ((d * sd + f * cd)^2)/tt; + t = t/gg; tt = tt/gg; + // The equation of the ellipse is t * (x - center.x)^2 + tt * (y - center.y)^2 = 1; + real aa, bb; + aa = sqrt(2 * (u - 2 * b * d * f - a * c * g)/dd); + bb = sqrt(2 * (u - 2 * b * d * f - a * c * g)/ddd); + a = t > tt ? max(aa, bb) : min(aa, bb); + b = t > tt ? min(aa, bb) : max(aa, bb); + return ellipse(point(R, (x, y)/R), + a, b, degrees(pi/2 - dir - angle(R.i))); +} + +/*<asyxml><function type="ellipse" signature="ellipse(point,point,point,point,point)"><code></asyxml>*/ +ellipse ellipse(point M1, point M2, point M3, point M4, point M5) +{/*<asyxml></code><documentation>Return the ellipse passing through the five points (if possible)</documentation></function></asyxml>*/ + return ellipse(bqe(M1, M2, M3, M4, M5)); +} + +/*<asyxml><function type="bool" signature="inside(ellipse,point)"><code></asyxml>*/ +bool inside(ellipse el, point M) +{/*<asyxml></code><documentation>Return 'true' iff 'M' is inside 'el'.</documentation></function></asyxml>*/ + return abs(el.F1 - M) + abs(el.F2 - M) - 2 * el.a < -epsgeo; +} + +/*<asyxml><function type="bool" signature="inside(parabola,point)"><code></asyxml>*/ +bool inside(parabola p, point M) +{/*<asyxml></code><documentation>Return 'true' if 'M' is inside 'p'.</documentation></function></asyxml>*/ + return distance(p.D, M) - abs(p.F - M) > epsgeo; +} + +/*<asyxml><function type="parabola" signature="parabola(point,line)"><code></asyxml>*/ +parabola parabola(point F, line l) +{/*<asyxml></code><documentation>Return the parabola whose focus is 'F' and directrix is 'l'.</documentation></function></asyxml>*/ + parabola op; + op.init(F, l); + return op; +} + +/*<asyxml><function type="parabola" signature="parabola(point,point)"><code></asyxml>*/ +parabola parabola(point F, point vertex) +{/*<asyxml></code><documentation>Return the parabola whose focus is 'F' and vertex is 'vertex'.</documentation></function></asyxml>*/ + parabola op; + point[] P = standardizecoordsys(F, vertex); + point A = rotate(180, P[1]) * P[0]; + point B = A + rotateO(90) * unit(P[1]-A); + op.init(P[0], line(A, B)); + return op; +} + +/*<asyxml><function type="parabola" signature="parabola(point,real,real)"><code></asyxml>*/ +parabola parabola(point F, real a, real angle) +{/*<asyxml></code><documentation>Return the parabola whose focus is F, latus rectum is 4a and + the angle of the axis of symmetry (in the coordinate system of F) is 'angle'.</documentation></function></asyxml>*/ + parabola op; + coordsys R = F.coordsys; + point A = F - point(R, R.polar(2a, radians(angle))); + point B = A + point(R, R.polar(1, radians(90 + angle))); + op.init(F, line(A, B)); + return op; +} + +/*<asyxml><function type="bool" signature="isparabola(bqe)"><code></asyxml>*/ +bool isparabola(bqe bqe) +{/*<asyxml></code><documentation>Return true iff 'bqe' is the equation of a parabola.</documentation></function></asyxml>*/ + bqe lbqe = changecoordsys(defaultcoordsys, bqe); + real a = lbqe.a[0], b = lbqe.a[1]/2, c = lbqe.a[2], d = lbqe.a[3]/2, f = lbqe.a[4]/2, g = lbqe.a[5]; + real delta = a * c * g + b * f * d + d * b * f - (b^2 * g + d^2 * c + f^2 * a); + return (abs(delta) > epsgeo && abs(b^2 - a * c) < epsgeo); +} + +/*<asyxml><function type="parabola" signature="parabola(bqe)"><code></asyxml>*/ +parabola parabola(bqe bqe) +{/*<asyxml></code><documentation>Return the parabola a[0]x^2 + a[1]xy + a[2]y^2 + a[3]x + a[4]y + a[5]] = 0 (a[n] means bqe.a[n]). + <url href = "http://mathworld.wolfram.com/QuadraticCurve.html"/> + <url href = "http://mathworld.wolfram.com/Parabola.html"/></documentation></function></asyxml>*/ + bqe lbqe = changecoordsys(defaultcoordsys, bqe); + real a = lbqe.a[0], b = lbqe.a[1]/2, c = lbqe.a[2], d = lbqe.a[3]/2, f = lbqe.a[4]/2, g = lbqe.a[5]; + string message = "parabola: the given equation is not an equation of a parabola."; + real delta = a * c * g + b * f * d + d * b * f - (b^2 * g + d^2 * c + f^2 * a); + if(abs(delta) < 10 * epsgeo || abs(b^2 - a * c) > 10 * epsgeo) abort(message); + real dir = abs(b) < epsgeo ? 0 : 0.5 * acot(0.5 * (c - a)/b); + if(dir * (c - a) * b < 0) dir = dir < 0 ? dir + pi/2 : dir - pi/2; + real cd = cos(dir), sd = sin(dir); + real ap = a * cd^2 - 2 * b * cd * sd + c * sd^2; + real cp = a * sd^2 + 2 * b * cd * sd + c * cd^2; + real dp = d * cd - f * sd; + real fp = d * sd + f * cd; + real gp = g; + parabola op; + coordsys R = bqe.coordsys; + // The equation of the parabola is ap * x'^2 + cp * y'^2 + 2dp * x'+2fp * y'+gp = 0 + if (abs(ap) < epsgeo) {/* directrix parallel to the rotated(dir) y-axis + equation: (y-vertex.y)^2 = 4 * a * (x-vertex) + */ + pair pvertex = rotate(degrees(-dir)) * (0.5(-gp + fp^2/cp)/dp, -fp/cp); + real a = -0.5 * dp/cp; + point vertex = point(R, pvertex/R); + point focus = point(R, (pvertex + a * expi(-dir))/R); + op = parabola(focus, vertex); + + } else {/* directrix parallel to the rotated(dir) x-axis + equation: (x-vertex)^2 = 4 * a * (y-vertex.y) + */ + pair pvertex = rotate(degrees(-dir)) * (-dp/ap, 0.5 * (-gp + dp^2/ap)/fp); + real a = -0.5 * fp/ap; + point vertex = point(R, pvertex/R); + point focus = point(R, (pvertex + a * expi(pi/2 - dir))/R); + op = parabola(focus, vertex); + } + return op; +} + +/*<asyxml><function type="parabola" signature="parabola(point,point,point,line)"><code></asyxml>*/ +parabola parabola(point M1, point M2, point M3, line l) +{/*<asyxml></code><documentation>Return the parabola passing through the three points with its directix + parallel to the line 'l'.</documentation></function></asyxml>*/ + coordsys R; + pair[] pts; + if (samecoordsys(M1, M2, M3)) { + R = M1.coordsys; + } else { + R = defaultcoordsys; + } + real gle = degrees(l); + coordsys Rp = cartesiansystem(R.O, rotate(gle) * R.i, rotate(gle) * R.j); + pts = new pair[] {coordinates(changecoordsys(Rp, M1)), + coordinates(changecoordsys(Rp, M2)), + coordinates(changecoordsys(Rp, M3))}; + real[][] M; + real[] x; + for (int i = 0; i < 3; ++i) { + M[i] = new real[] {pts[i].x, pts[i].y, 1}; + x[i] = -pts[i].x^2; + } + real[] coef = solve(M, x); + return parabola(changecoordsys(R, bqe(Rp, 1, 0, 0, coef[0], coef[1], coef[2]))); +} + +/*<asyxml><function type="parabola" signature="parabola(point,point,point,point,point)"><code></asyxml>*/ +parabola parabola(point M1, point M2, point M3, point M4, point M5) +{/*<asyxml></code><documentation>Return the parabola passing through the five points.</documentation></function></asyxml>*/ + return parabola(bqe(M1, M2, M3, M4, M5)); +} + +/*<asyxml><function type="hyperbola" signature="hyperbola(point,real,real,real)"><code></asyxml>*/ +hyperbola hyperbola(point C, real a, real b, real angle = 0) +{/*<asyxml></code><documentation>Return the hyperbola centered at 'C' with semimajor axis 'a' along C--C + dir(angle), + semiminor axis 'b' along the perpendicular.</documentation></function></asyxml>*/ + hyperbola oh; + coordsys R = C.coordsys; + angle += degrees(R.i); + real c = sqrt(a^2 + b^2); + point f1 = point(R, (locate(C) + rotate(angle) * (-c, 0))/R); + point f2 = point(R, (locate(C) + rotate(angle) * (c, 0))/R); + oh.init(f1, f2, a); + return oh; +} + +/*<asyxml><function type="hyperbola" signature="hyperbola(bqe)"><code></asyxml>*/ +hyperbola hyperbola(bqe bqe) +{/*<asyxml></code><documentation>Return the hyperbola a[0]x^2 + a[1]xy + a[2]y^2 + a[3]x + a[4]y + a[5]] = 0 (a[n] means bqe.a[n]). + <url href = "http://mathworld.wolfram.com/QuadraticCurve.html"/> + <url href = "http://mathworld.wolfram.com/Hyperbola.html"/></documentation></function></asyxml>*/ + bqe lbqe = changecoordsys(defaultcoordsys, bqe); + real a = lbqe.a[0], b = lbqe.a[1]/2, c = lbqe.a[2], d = lbqe.a[3]/2, f = lbqe.a[4]/2, g = lbqe.a[5]; + string message = "hyperbola: the given equation is not an equation of a hyperbola."; + real delta = a * c * g + b * f * d + d * b * f - (b^2 * g + d^2 * c + f^2 * a); + if(abs(delta) < 10 * epsgeo || abs(b^2 - a * c) < 0) abort(message); + real dir = abs(b) < epsgeo ? 0 : 0.5 * acot(0.5 * (c - a)/b); + real cd = cos(dir), sd = sin(dir); + real ap = a * cd^2 - 2 * b * cd * sd + c * sd^2; + real cp = a * sd^2 + 2 * b * cd * sd + c * cd^2; + real dp = d * cd - f * sd; + real fp = d * sd + f * cd; + real gp = -g + dp^2/ap + fp^2/cp; + hyperbola op; + coordsys R = bqe.coordsys; + real j = b^2 - a * c; + point C = point(R, ((c * d - b * f)/j, (a * f - b * d)/j)/R); + real aa = gp/ap, bb = gp/cp; + real a = sqrt(abs(aa)), b = sqrt(abs(bb)); + if(aa < 0) {dir -= pi/2; aa = a; a = b; b = aa;} + return hyperbola(C, a, b, degrees(-dir - angle(R.i))); +} + +/*<asyxml><function type="hyperbola" signature="hyperbola(point,point,point,point,point)"><code></asyxml>*/ +hyperbola hyperbola(point M1, point M2, point M3, point M4, point M5) +{/*<asyxml></code><documentation>Return the hyperbola passing through the five points (if possible).</documentation></function></asyxml>*/ + return hyperbola(bqe(M1, M2, M3, M4, M5)); +} + +/*<asyxml><function type="hyperbola" signature="conj(hyperbola)"><code></asyxml>*/ +hyperbola conj(hyperbola h) +{/*<asyxml></code><documentation>Conjugate.</documentation></function></asyxml>*/ + return hyperbola(h.C, h.b, h.a, 90 + h.angle); +} + +/*<asyxml><function type="circle" signature="circle(explicit point,real)"><code></asyxml>*/ +circle circle(explicit point C, real r) +{/*<asyxml></code><documentation>Circle given by center and radius.</documentation></function></asyxml>*/ + circle oc = new circle; + oc.C = C; + oc.r = r; + if(!finite(r)) oc.l = line(C, C + vector(C.coordsys, (1, 0))); + return oc; +} + +/*<asyxml><function type="circle" signature="circle(point,point)"><code></asyxml>*/ +circle circle(point A, point B) +{/*<asyxml></code><documentation>Return the circle of diameter AB.</documentation></function></asyxml>*/ + real r; + circle oc; + real a = abs(A), b = abs(B); + if(finite(a) && finite(b)) { + oc = circle((A + B)/2, abs(A - B)/2); + } else { + oc.r = infinity; + if(finite(abs(A))) oc.l = line(A, A + unit(B)); + else { + if(finite(abs(B))) oc.l = line(B, B + unit(A)); + else if(finite(abs(A - B)/2)) oc = circle((A + B)/2, abs(A - B)/2); else + oc.l = line(A, B); + } + } + return oc; +} + +/*<asyxml><function type="circle" signature="circle(segment)"><code></asyxml>*/ +circle circle(segment s) +{/*<asyxml></code><documentation>Return the circle of diameter 's'.</documentation></function></asyxml>*/ + return circle(s.A, s.B); +} + +/*<asyxml><function type="point" signature="circumcenter(point,point,point)"><code></asyxml>*/ +point circumcenter(point A, point B, point C) +{/*<asyxml></code><documentation>Return the circumcenter of triangle ABC.</documentation></function></asyxml>*/ + point[] P = standardizecoordsys(A, B, C); + coordsys R = P[0].coordsys; + pair a = A, b = B, c = C; + pair mAB = (a + b)/2; + pair mAC = (a + c)/2; + pair pp = extension(mAB, rotate(90, mAB) * a, mAC, rotate(90, mAC) * c); + return point(R, pp/R); +} + +/*<asyxml><function type="circle" signature="circle(point,point,point)"><code></asyxml>*/ +circle circle(point A, point B, point C) +{/*<asyxml></code><documentation>Return the circumcircle of the triangle ABC.</documentation></function></asyxml>*/ + if(collinear(A - B, A - C)) { + circle oc; + oc.r = infinity; + oc.C = (A + B + C)/3; + oc.l = line(oc.C, oc.C == A ? B : A); + return oc; + } + point c = circumcenter(A, B, C); + return circle(c, abs(c - A)); +} + +/*<asyxml><function type="circle" signature="circumcircle(point,point,point)"><code></asyxml>*/ +circle circumcircle(point A, point B, point C) +{/*<asyxml></code><documentation>Return the circumcircle of the triangle ABC.</documentation></function></asyxml>*/ + return circle(A, B, C); +} + +/*<asyxml><operator type = "circle" signature="*(real,explicit circle)"><code></asyxml>*/ +circle operator *(real x, explicit circle c) +{/*<asyxml></code><documentation>Multiply the radius of 'c'.</documentation></operator></asyxml>*/ + return finite(c.r) ? circle(c.C, x * c.r) : c; +} +circle operator *(int x, explicit circle c) +{ + return finite(c.r) ? circle(c.C, x * c.r) : c; +} +/*<asyxml><operator type = "circle" signature="/(explicit circle,real)"><code></asyxml>*/ +circle operator /(explicit circle c, real x) +{/*<asyxml></code><documentation>Divide the radius of 'c'</documentation></operator></asyxml>*/ + return finite(c.r) ? circle(c.C, c.r/x) : c; +} +circle operator /(explicit circle c, int x) +{ + return finite(c.r) ? circle(c.C, c.r/x) : c; +} +/*<asyxml><operator type = "circle" signature="+(explicit circle,explicit point)"><code></asyxml>*/ +circle operator +(explicit circle c, explicit point M) +{/*<asyxml></code><documentation>Translation of 'c'.</documentation></operator></asyxml>*/ + return circle(c.C + M, c.r); +} +/*<asyxml><operator type = "circle" signature="-(explicit circle,explicit point)"><code></asyxml>*/ +circle operator -(explicit circle c, explicit point M) +{/*<asyxml></code><documentation>Translation of 'c'.</documentation></operator></asyxml>*/ + return circle(c.C - M, c.r); +} +/*<asyxml><operator type = "circle" signature="+(explicit circle,pair)"><code></asyxml>*/ +circle operator +(explicit circle c, pair m) +{/*<asyxml></code><documentation>Translation of 'c'. + 'm' represent coordinates in the coordinate system where 'c' is defined.</documentation></operator></asyxml>*/ + return circle(c.C + m, c.r); +} +/*<asyxml><operator type = "circle" signature="-(explicit circle,pair)"><code></asyxml>*/ +circle operator -(explicit circle c, pair m) +{/*<asyxml></code><documentation>Translation of 'c'. + 'm' represent coordinates in the coordinate system where 'c' is defined.</documentation></operator></asyxml>*/ + return circle(c.C - m, c.r); +} +/*<asyxml><operator type = "circle" signature="+(explicit circle,vector)"><code></asyxml>*/ +circle operator +(explicit circle c, vector m) +{/*<asyxml></code><documentation>Translation of 'c'.</documentation></operator></asyxml>*/ + return circle(c.C + m, c.r); +} +/*<asyxml><operator type = "circle" signature="-(explicit circle,vector)"><code></asyxml>*/ +circle operator -(explicit circle c, vector m) +{/*<asyxml></code><documentation>Translation of 'c'.</documentation></operator></asyxml>*/ + return circle(c.C - m, c.r); +} +/*<asyxml><operator type = "real" signature="^(point,explicit circle)"><code></asyxml>*/ +real operator ^(point M, explicit circle c) +{/*<asyxml></code><documentation>The power of 'M' with respect to the circle 'c'</documentation></operator></asyxml>*/ + return xpart((abs(locate(M) - locate(c.C)), c.r)^2); +} +/*<asyxml><operator type = "bool" signature="@(point,explicit circle)"><code></asyxml>*/ +bool operator @(point M, explicit circle c) +{/*<asyxml></code><documentation>Return true iff 'M' is on the circle 'c'.</documentation></operator></asyxml>*/ + return finite(c.r) ? + abs(abs(locate(M) - locate(c.C)) - abs(c.r)) <= 10 * epsgeo : + M @ c.l; +} + +/*<asyxml><operator type = "ellipse" signature="cast(circle)"><code></asyxml>*/ +ellipse operator cast(circle c) +{/*<asyxml></code><documentation></documentation></operator></asyxml>*/ + return finite(c.r) ? ellipse(c.C, c.r, c.r, 0) : ellipse(c.l.A, c.l.B, infinity); +} + +/*<asyxml><operator type = "circle" signature="cast(ellipse)"><code></asyxml>*/ +circle operator cast(ellipse el) +{/*<asyxml></code><documentation></documentation></operator></asyxml>*/ + circle oc; + bool infb = (!finite(el.a) || !finite(el.b)); + if(!infb && abs(el.a - el.b) > epsgeo) + abort("Can not cast ellipse with different axis values to circle"); + oc = circle(el.C, infb ? infinity : el.a); + oc.l = el.l.copy(); + return oc; +} + +/*<asyxml><operator type = "ellipse" signature="cast(conic)"><code></asyxml>*/ +ellipse operator cast(conic co) +{/*<asyxml></code><documentation>Cast a conic to an ellipse (can be a circle).</documentation></operator></asyxml>*/ + if(degenerate(co) && co.e < 1) return ellipse(co.l[0].A, co.l[0].B, infinity); + ellipse oe; + if(co.e < 1) { + real a = co.p/(1 - co.e^2); + real c = co.e * a; + vector v = co.D.v; + if(!sameside(co.D.A + v, co.F, co.D)) v = -v; + point f2 = co.F + 2 * c * v; + f2 = changecoordsys(co.F.coordsys, f2); + oe = a == 0 ? ellipse(co.F, co.p, co.p, 0) : ellipse(co.F, f2, a); + } else + abort("casting: The conic section is not an ellipse."); + return oe; +} + +/*<asyxml><operator type = "parabola" signature="cast(conic)"><code></asyxml>*/ +parabola operator cast(conic co) +{/*<asyxml></code><documentation>Cast a conic to a parabola.</documentation></operator></asyxml>*/ + parabola op; + if(abs(co.e - 1) > epsgeo) abort("casting: The conic section is not a parabola."); + op.init(co.F, co.D); + return op; +} + +/*<asyxml><operator type = "conic" signature="cast(parabola)"><code></asyxml>*/ +conic operator cast(parabola p) +{/*<asyxml></code><documentation>Cast a parabola to a conic section.</documentation></operator></asyxml>*/ + return conic(p.F, p.D, 1); +} + +/*<asyxml><operator type = "hyperbola" signature="cast(conic)"><code></asyxml>*/ +hyperbola operator cast(conic co) +{/*<asyxml></code><documentation>Cast a conic section to an hyperbola.</documentation></operator></asyxml>*/ + hyperbola oh; + if(co.e > 1) { + real a = co.p/(co.e^2 - 1); + real c = co.e * a; + vector v = co.D.v; + if(sameside(co.D.A + v, co.F, co.D)) v = -v; + point f2 = co.F + 2 * c * v; + f2 = changecoordsys(co.F.coordsys, f2); + oh = hyperbola(co.F, f2, a); + } else + abort("casting: The conic section is not an hyperbola."); + return oh; +} + +/*<asyxml><operator type = "conic" signature="cast(hyperbola)"><code></asyxml>*/ +conic operator cast(hyperbola h) +{/*<asyxml></code><documentation>Hyperbola to conic section.</documentation></operator></asyxml>*/ + return conic(h.F1, h.D1, h.e); +} + +/*<asyxml><operator type = "conic" signature="cast(ellipse)"><code></asyxml>*/ +conic operator cast(ellipse el) +{/*<asyxml></code><documentation>Ellipse to conic section.</documentation></operator></asyxml>*/ + conic oc; + if(abs(el.c) > epsgeo) { + real x = el.a^2/el.c; + point O = (el.F1 + el.F2)/2; + point A = O + x * unit(el.F1 - el.F2); + oc = conic(el.F1, perpendicular(A, line(el.F1, el.F2)), el.e); + } else {//The ellipse is a circle + coordsys R = coordsys(el); + point M = el.F1 + point(R, R.polar(el.a, 0)); + line l = line(rotate(90, M) * el.F1, M); + oc = conic(el.F1, l, 0); + } + if(degenerate(el)) { + oc.p = infinity; + oc.h = infinity; + oc.l = new line[]{el.l}; + } + return oc; +} + +/*<asyxml><operator type = "conic" signature="cast(circle)"><code></asyxml>*/ +conic operator cast(circle c) +{/*<asyxml></code><documentation>Circle to conic section.</documentation></operator></asyxml>*/ + return (conic)((ellipse)c); +} + +/*<asyxml><operator type = "circle" signature="cast(conic)"><code></asyxml>*/ +circle operator cast(conic c) +{/*<asyxml></code><documentation>Conic section to circle.</documentation></operator></asyxml>*/ + ellipse el = (ellipse)c; + circle oc; + if(abs(el.a - el.b) < epsgeo) { + oc = circle(el.C, el.a); + if(degenerate(c)) oc.l = c.l[0]; + } + else abort("Can not cast this conic to a circle"); + return oc; +} + +/*<asyxml><operator type = "ellipse" signature="*(transform,ellipse)"><code></asyxml>*/ +ellipse operator *(transform t, ellipse el) +{/*<asyxml></code><documentation>Provide transform * ellipse.</documentation></operator></asyxml>*/ + if(!degenerate(el)) { + point[] ep; + for (int i = 0; i < 360; i += 72) { + ep.push(t * angpoint(el, i)); + } + ellipse oe = ellipse(ep[0], ep[1], ep[2], ep[3], ep[4]); + if(angpoint(oe, 0) != ep[0]) return ellipse(oe.F2, oe.F1, oe.a); + return oe; + } + return ellipse(t * el.l.A, t * el.l.B, infinity); +} + +/*<asyxml><operator type = "parabola" signature="*(transform,parabola)"><code></asyxml>*/ +parabola operator *(transform t, parabola p) +{/*<asyxml></code><documentation>Provide transform * parabola.</documentation></operator></asyxml>*/ + point[] P; + P.push(t * angpoint(p, 45)); + P.push(t * angpoint(p, -45)); + P.push(t * angpoint(p, 180)); + parabola op = parabola(P[0], P[1], P[2], t * p.D); + op.bmin = p.bmin; + op.bmax = p.bmax; + + return op; +} + +/*<asyxml><operator type = "ellipse" signature="*(transform,circle)"><code></asyxml>*/ +ellipse operator *(transform t, circle c) +{/*<asyxml></code><documentation>Provide transform * circle. + For example, 'circle C = scale(2) * circle' and 'ellipse E = xscale(2) * circle' are valid + but 'circle C = xscale(2) * circle' is invalid.</documentation></operator></asyxml>*/ + return t * ((ellipse)c); +} + +/*<asyxml><operator type = "hyperbola" signature="*(transform,hyperbola)"><code></asyxml>*/ +hyperbola operator *(transform t, hyperbola h) +{/*<asyxml></code><documentation>Provide transform * hyperbola.</documentation></operator></asyxml>*/ + if (t == identity()) { + return h; + } + + point[] ep; + for (int i = 90; i <= 270; i += 45) { + ep.push(t * angpoint(h, i)); + } + + hyperbola oe = hyperbola(ep[0], ep[1], ep[2], ep[3], ep[4]); + if(angpoint(oe, 90) != ep[0]) { + oe = hyperbola(oe.F2, oe.F1, oe.a); + } + + oe.bmin = h.bmin; + oe.bmax = h.bmax; + + return oe; +} + +/*<asyxml><operator type = "conic" signature="*(transform,conic)"><code></asyxml>*/ +conic operator *(transform t, conic co) +{/*<asyxml></code><documentation>Provide transform * conic.</documentation></operator></asyxml>*/ + if(co.e < 1) return (t * ((ellipse)co)); + if(co.e == 1) return (t * ((parabola)co)); + return (t * ((hyperbola)co)); +} + +/*<asyxml><operator type = "ellipse" signature="*(real,ellipse)"><code></asyxml>*/ +ellipse operator *(real x, ellipse el) +{/*<asyxml></code><documentation>Identical but more efficient (rapid) than 'scale(x, el.C) * el'.</documentation></operator></asyxml>*/ + return degenerate(el) ? el : ellipse(el.C, x * el.a, x * el.b, el.angle); +} + +/*<asyxml><operator type = "ellipse" signature="/(ellipse,real)"><code></asyxml>*/ +ellipse operator /(ellipse el, real x) +{/*<asyxml></code><documentation>Identical but more efficient (rapid) than 'scale(1/x, el.C) * el'.</documentation></operator></asyxml>*/ + return degenerate(el) ? el : ellipse(el.C, el.a/x, el.b/x, el.angle); +} + +/*<asyxml><function type="path" signature="arcfromcenter(ellipse,real,real,int,bool)"><code></asyxml>*/ +path arcfromcenter(ellipse el, real angle1, real angle2, + bool direction=CCW, + int n=ellipsenodesnumber(el.a,el.b,angle1,angle2,direction)) +{/*<asyxml></code><documentation>Return the path of the ellipse 'el' from angle1 to angle2 in degrees, + drawing in the given direction, with n nodes. + The angles are mesured relatively to the axis (C,x-axis) where C is + the center of the ellipse.</documentation></function></asyxml>*/ + if(degenerate(el)) abort("arcfromcenter: can not convert degenerated ellipse to path."); + if (angle1 > angle2) + return reverse(arcfromcenter(el, angle2, angle1, !direction, n)); + + guide op; + coordsys Rp=coordsys(el); + if (n < 1) return op; + + interpolate join = operator ..; + real stretch = max(el.a/el.b, el.b/el.a); + + if (stretch > 10) { + n *= floor(stretch/5); + join = operator --; + } + + real a1 = direction ? radians(angle1) : radians(angle2); + real a2 = direction ? radians(angle2) : radians(angle1) + 2 * pi; + real step=(a2 - a1)/(n != 1 ? n-1 : 1); + real a, r; + real da = radians(el.angle); + + for (int i=0; i < n; ++i) { + a = a1 + i * step; + r = el.b/sqrt(1 - (el.e * cos(a))^2); + op = join(op, Rp*Rp.polar(r, da + a)); + } + + return shift(el.C.x*Rp.i + el.C.y*Rp.j) * (direction ? op : reverse(op)); +} + +/*<asyxml><function type="path" signature="arcfromcenter(hyperbola,real,real,int,bool)"><code></asyxml>*/ +path arcfromcenter(hyperbola h, real angle1, real angle2, + int n = hyperbolanodesnumber(h, angle1, angle2), + bool direction = CCW) +{/*<asyxml></code><documentation>Return the path of the hyperbola 'h' from angle1 to angle2 in degrees, + drawing in the given direction, with n nodes. + The angles are mesured relatively to the axis (C, x-axis) where C is + the center of the hyperbola.</documentation></function></asyxml>*/ + guide op; + coordsys Rp = coordsys(h); + if (n < 1) return op; + if (angle1 > angle2) { + path g = reverse(arcfromcenter(h, angle2, angle1, n, !direction)); + return g == nullpath ? g : reverse(g); + } + real a1 = direction ? radians(angle1) : radians(angle2); + real a2 = direction ? radians(angle2) : radians(angle1) + 2 * pi; + real step = (a2 - a1)/(n != 1 ? n - 1 : 1); + real a, r; + typedef guide interpolate(... guide[]); + interpolate join = operator ..; + real da = radians(h.angle); + for (int i = 0; i < n; ++i) { + a = a1 + i * step; + r = (h.b * cos(a))^2 - (h.a * sin(a))^2; + if(r > epsgeo) { + r = sqrt(h.a^2 * h.b^2/r); + op = join(op, Rp * Rp.polar(r, a + da)); + join = operator ..; + } else join = operator --; + } + return shift(h.C.x * Rp.i + h.C.y * Rp.j)* + (direction ? op : op == nullpath ? op : reverse(op)); +} + +/*<asyxml><function type="path" signature="arcfromcenter(explicit conic,real,real,int,bool)"><code></asyxml>*/ +path arcfromcenter(explicit conic co, real angle1, real angle2, + int n, bool direction = CCW) +{/*<asyxml></code><documentation>Use arcfromcenter(ellipse, ...) or arcfromcenter(hyperbola, ...) depending of + the eccentricity of 'co'.</documentation></function></asyxml>*/ + path g; + if(co.e < 1) + g = arcfromcenter((ellipse)co, angle1, + angle2, direction, n); + else if(co.e > 1) + g = arcfromcenter((hyperbola)co, angle1, + angle2, n, direction); + else abort("arcfromcenter: does not exist for a parabola."); + return g; +} + +/*<asyxml><constant type = "polarconicroutine" signature="fromCenter"><code></asyxml>*/ +restricted polarconicroutine fromCenter = arcfromcenter;/*<asyxml></code><documentation></documentation></constant></asyxml>*/ +/*<asyxml><constant type = "polarconicroutine" signature="fromFocus"><code></asyxml>*/ +restricted polarconicroutine fromFocus = arcfromfocus;/*<asyxml></code><documentation></documentation></constant></asyxml>*/ + +/*<asyxml><function type="bqe" signature="equation(ellipse)"><code></asyxml>*/ +bqe equation(ellipse el) +{/*<asyxml></code><documentation>Return the coefficients of the equation of the ellipse in its coordinate system: + bqe.a[0] * x^2 + bqe.a[1] * x * y + bqe.a[2] * y^2 + bqe.a[3] * x + bqe.a[4] * y + bqe.a[5] = 0. + One can change the coordinate system of 'bqe' using the routine 'changecoordsys'.</documentation></function></asyxml>*/ + pair[] pts; + for (int i = 0; i < 360; i += 72) + pts.push(locate(angpoint(el, i))); + + real[][] M; + real[] x; + for (int i = 0; i < 5; ++i) { + M[i] = new real[] {pts[i].x * pts[i].y, pts[i].y^2, pts[i].x, pts[i].y, 1}; + x[i] = -pts[i].x^2; + } + real[] coef = solve(M, x); + bqe bqe = changecoordsys(coordsys(el), + bqe(defaultcoordsys, + 1, coef[0], coef[1], coef[2], coef[3], coef[4])); + bqe.a = approximate(bqe.a); + return bqe; +} + +/*<asyxml><function type="bqe" signature="equation(parabola)"><code></asyxml>*/ +bqe equation(parabola p) +{/*<asyxml></code><documentation>Return the coefficients of the equation of the parabola in its coordinate system. + bqe.a[0] * x^2 + bqe.a[1] * x * y + bqe.a[2] * y^2 + bqe.a[3] * x + bqe.a[4] * y + bqe.a[5] = 0 + One can change the coordinate system of 'bqe' using the routine 'changecoordsys'.</documentation></function></asyxml>*/ + coordsys R = canonicalcartesiansystem(p); + parabola tp = changecoordsys(R, p); + point A = projection(tp.D) * point(R, (0, 0)); + real a = abs(A); + return changecoordsys(coordsys(p), + bqe(R, 0, 0, 1, -4 * a, 0, 0)); +} + +/*<asyxml><function type="bqe" signature="equation(hyperbola)"><code></asyxml>*/ +bqe equation(hyperbola h) +{/*<asyxml></code><documentation>Return the coefficients of the equation of the hyperbola in its coordinate system. + bqe.a[0] * x^2 + bqe.a[1] * x * y + bqe.a[2] * y^2 + bqe.a[3] * x + bqe.a[4] * y + bqe.a[5] = 0 + One can change the coordinate system of 'bqe' using the routine 'changecoordsys'.</documentation></function></asyxml>*/ + coordsys R = canonicalcartesiansystem(h); + return changecoordsys(coordsys(h), + bqe(R, 1/h.a^2, 0, -1/h.b^2, 0, 0, -1)); +} + +/*<asyxml><operator type = "path" signature="cast(ellipse)"><code></asyxml>*/ +path operator cast(ellipse el) +{/*<asyxml></code><documentation>Cast ellipse to path.</documentation></operator></asyxml>*/ + if(degenerate(el)) + abort("Casting degenerated ellipse to path is not possible."); + int n = el.e == 0 ? circlenodesnumber(el.a) : ellipsenodesnumber(el.a, el.b); + return arcfromcenter(el, 0.0, 360, CCW, n)&cycle; +} + +/*<asyxml><operator type = "path" signature="cast(circle)"><code></asyxml>*/ +path operator cast(circle c) +{/*<asyxml></code><documentation>Cast circle to path.</documentation></operator></asyxml>*/ + return (path)((ellipse)c); +} + +/*<asyxml><function type="real[]" signature="bangles(picture,parabola)"><code></asyxml>*/ +real[] bangles(picture pic = currentpicture, parabola p) +{/*<asyxml></code><documentation>Return the array {ma, Ma} where 'ma' and 'Ma' are respectively + the smaller and the larger angles for which the parabola 'p' is included + in the bounding box of the picture 'pic'.</documentation></function></asyxml>*/ + pair bmin, bmax; + pair[] b; + if (p.bmin == p.bmax) { + bmin = pic.userMin(); + bmax = pic.userMax(); + } else { + bmin = p.bmin;bmax = p.bmax; + } + if(bmin.x == bmax.x || bmin.y == bmax.y || !finite(abs(bmin)) || !finite(abs(bmax))) + return new real[] {0, 0}; + b[0] = bmin; + b[1] = (bmax.x, bmin.y); + b[2] = bmax; + b[3] = (bmin.x, bmax.y); + real[] eq = changecoordsys(defaultcoordsys, equation(p)).a; + pair[] inter; + for (int i = 0; i < 4; ++i) { + pair[] tmp = intersectionpoints(b[i], b[(i + 1)%4], eq); + for (int j = 0; j < tmp.length; ++j) { + if(dot(b[i]-tmp[j], b[(i + 1)%4]-tmp[j]) <= epsgeo) + inter.push(tmp[j]); + } + } + pair F = p.F, V = p.V; + real d = degrees(F - V); + real[] a = sequence(new real(int n){ + return (360 - d + degrees(inter[n]-F))%360; + }, inter.length); + real ma = a.length != 0 ? min(a) : 0, Ma= a.length != 0 ? max(a) : 0; + return new real[] {ma, Ma}; +} + +/*<asyxml><function type="real[][]" signature="bangles(picture,hyperbola)"><code></asyxml>*/ +real[][] bangles(picture pic = currentpicture, hyperbola h) +{/*<asyxml></code><documentation>Return the array {{ma1, Ma1}, {ma2, Ma2}} where 'maX' and 'MaX' are respectively + the smaller and the bigger angles (from h.FX) for which the hyperbola 'h' is included + in the bounding box of the picture 'pic'.</documentation></function></asyxml>*/ + pair bmin, bmax; + pair[] b; + if (h.bmin == h.bmax) { + bmin = pic.userMin(); + bmax = pic.userMax(); + } else { + bmin = h.bmin;bmax = h.bmax; + } + if(bmin.x == bmax.x || bmin.y == bmax.y || !finite(abs(bmin)) || !finite(abs(bmax))) + return new real[][] {{0, 0}, {0, 0}}; + b[0] = bmin; + b[1] = (bmax.x, bmin.y); + b[2] = bmax; + b[3] = (bmin.x, bmax.y); + real[] eq = changecoordsys(defaultcoordsys, equation(h)).a; + pair[] inter0, inter1; + pair C = locate(h.C); + pair F1 = h.F1; + for (int i = 0; i < 4; ++i) { + pair[] tmp = intersectionpoints(b[i], b[(i + 1)%4], eq); + for (int j = 0; j < tmp.length; ++j) { + if(dot(b[i]-tmp[j], b[(i + 1)%4]-tmp[j]) <= epsgeo) { + if(dot(F1 - C, tmp[j]-C) > 0) inter0.push(tmp[j]); + else inter1.push(tmp[j]); + } + } + } + real d = degrees(F1 - C); + real[] ma, Ma; + pair[][] inter = new pair[][] {inter0, inter1}; + for (int i = 0; i < 2; ++i) { + real[] a = sequence(new real(int n){ + return (360 - d + degrees(inter[i][n]-F1))%360; + }, inter[i].length); + ma[i] = a.length != 0 ? min(a) : 0; + Ma[i] = a.length != 0 ? max(a) : 0; + } + return new real[][] {{ma[0], Ma[0]}, {ma[1], Ma[1]}}; +} + +/*<asyxml><operator type = "path" signature="cast(parabola)"><code></asyxml>*/ +path operator cast(parabola p) +{/*<asyxml></code><documentation>Cast parabola to path. + If possible, the returned path is restricted to the actual bounding box + of the current picture if the variables 'p.bmin' and 'p.bmax' are not set else + the bounding box of box(p.bmin, p.bmax) is used instead.</documentation></operator></asyxml>*/ + real[] bangles = bangles(p); + int n = parabolanodesnumber(p, bangles[0], bangles[1]); + return arcfromfocus(p, bangles[0], bangles[1], n, CCW); +} + + +/*<asyxml><function type="void" signature="draw(picture,Label,circle,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/ +void draw(picture pic = currentpicture, Label L = "", circle c, + align align = NoAlign, pen p = currentpen, + arrowbar arrow = None, arrowbar bar = None, + margin margin = NoMargin, Label legend = "", marker marker = nomarker) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + if(degenerate(c)) draw(pic, L, c.l, align, p, arrow, legend, marker); + else draw(pic, L, (path)c, align, p, arrow, bar, margin, legend, marker); +} + +/*<asyxml><function type="void" signature="draw(picture,Label,ellipse,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/ +void draw(picture pic = currentpicture, Label L = "", ellipse el, + align align = NoAlign, pen p = currentpen, + arrowbar arrow = None, arrowbar bar = None, + margin margin = NoMargin, Label legend = "", marker marker = nomarker) +{/*<asyxml></code><documentation></documentation>Draw the ellipse 'el' if it is not degenerated else draw 'el.l'.</function></asyxml>*/ + if(degenerate(el)) draw(pic, L, el.l, align, p, arrow, legend, marker); + else draw(pic, L, (path)el, align, p, arrow, bar, margin, legend, marker); +} + +/*<asyxml><function type="void" signature="draw(picture,Label,parabola,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/ +void draw(picture pic = currentpicture, Label L = "", parabola parabola, + align align = NoAlign, pen p = currentpen, + arrowbar arrow = None, arrowbar bar = None, + margin margin = NoMargin, Label legend = "", marker marker = nomarker) +{/*<asyxml></code><documentation>Draw the parabola 'p' on 'pic' without (if possible) altering the + size of picture pic.</documentation></function></asyxml>*/ + pic.add(new void (frame f, transform t, transform T, pair m, pair M) { + // Reduce the bounds by the size of the pen and the margins. + m -= min(p); M -= max(p); + parabola.bmin = inverse(t) * m; + parabola.bmax = inverse(t) * M; + picture tmp; + path pp = t * ((path) (T * parabola)); + + if (pp != nullpath) { + draw(tmp, L, pp, align, p, arrow, bar, NoMargin, legend, marker); + add(f, tmp.fit()); + } + }, true); + + pair m = pic.userMin(), M = pic.userMax(); + if(m != M) { + pic.addBox(truepoint(SW), truepoint(NE)); + } +} + +/*<asyxml><operator type = "path" signature="cast(hyperbola)"><code></asyxml>*/ +path operator cast(hyperbola h) +{/*<asyxml></code><documentation>Cast hyperbola to path. + If possible, the returned path is restricted to the actual bounding box + of the current picture unless the variables 'h.bmin' and 'h.bmax' + are set; in this case the bounding box of box(h.bmin, h.bmax) is used instead. + Only the branch on the side of 'h.F1' is considered.</documentation></operator></asyxml>*/ + real[][] bangles = bangles(h); + int n = hyperbolanodesnumber(h, bangles[0][0], bangles[0][1]); + return arcfromfocus(h, bangles[0][0], bangles[0][1], n, CCW); +} + +/*<asyxml><function type="void" signature="draw(picture,Label,hyperbola,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/ +void draw(picture pic = currentpicture, Label L = "", hyperbola h, + align align = NoAlign, pen p = currentpen, + arrowbar arrow = None, arrowbar bar = None, + margin margin = NoMargin, Label legend = "", marker marker = nomarker) +{/*<asyxml></code><documentation>Draw the hyperbola 'h' on 'pic' without (if possible) altering the + size of the picture pic.</documentation></function></asyxml>*/ + pic.add(new void (frame f, transform t, transform T, pair m, pair M) { + // Reduce the bounds by the size of the pen and the margins. + m -= min(p); M -= max(p); + h.bmin = inverse(t) * m; + h.bmax = inverse(t) * M; + path hp; + + picture tmp; + hp = t * ((path) (T * h)); + if (hp != nullpath) { + draw(tmp, L, hp, align, p, arrow, bar, NoMargin, legend, marker); + } + + hyperbola ht = hyperbola(h.F2, h.F1, h.a); + ht.bmin = h.bmin; + ht.bmax = h.bmax; + + hp = t * ((path) (T * ht)); + if (hp != nullpath) { + draw(tmp, "", hp, align, p, arrow, bar, NoMargin, marker); + } + + add(f, tmp.fit()); + }, true); + + pair m = pic.userMin(), M = pic.userMax(); + if(m != M) + pic.addBox(truepoint(SW), truepoint(NE)); +} + +/*<asyxml><function type="void" signature="draw(picture,Label,explicit conic,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/ +void draw(picture pic = currentpicture, Label L = "", explicit conic co, + align align = NoAlign, pen p = currentpen, + arrowbar arrow = None, arrowbar bar = None, + margin margin = NoMargin, Label legend = "", marker marker = nomarker) +{/*<asyxml></code><documentation>Use one of the routine 'draw(ellipse, ...)', + 'draw(parabola, ...)' or 'draw(hyperbola, ...)' depending of the value of eccentricity of 'co'.</documentation></function></asyxml>*/ + if(co.e == 0) + draw(pic, L, (circle)co, align, p, arrow, bar, margin, legend, marker); + else + if(co.e < 1) draw(pic, L, (ellipse)co, align, p, arrow, bar, margin, legend, marker); + else + if(co.e == 1) draw(pic, L, (parabola)co, align, p, arrow, bar, margin, legend, marker); + else + if(co.e > 1) draw(pic, L, (hyperbola)co, align, p, arrow, bar, margin, legend, marker); + else abort("draw: unknown conic."); +} + +/*<asyxml><function type="int" signature="conicnodesnumber(conic,real,real)"><code></asyxml>*/ +int conicnodesnumber(conic co, real angle1, real angle2, bool dir = CCW) +{/*<asyxml></code><documentation>Return the number of node to draw a conic arc.</documentation></function></asyxml>*/ + int oi; + if(co.e == 0) { + circle c = (circle)co; + oi = circlenodesnumber(c.r, angle1, angle2); + } else if(co.e < 1) { + ellipse el = (ellipse)co; + oi = ellipsenodesnumber(el.a, el.b, angle1, angle2, dir); + } else if(co.e == 1) { + parabola p = (parabola)co; + oi = parabolanodesnumber(p, angle1, angle2); + } else { + hyperbola h = (hyperbola)co; + oi = hyperbolanodesnumber(h, angle1, angle2); + } + return oi; +} + +/*<asyxml><operator type = "path" signature="cast(conic)"><code></asyxml>*/ +path operator cast(conic co) +{/*<asyxml></code><documentation>Cast conic section to path.</documentation></operator></asyxml>*/ + if(co.e < 1) return (path)((ellipse)co); + if(co.e == 1) return (path)((parabola)co); + return (path)((hyperbola)co); +} + +/*<asyxml><function type="bqe" signature="equation(explicit conic)"><code></asyxml>*/ +bqe equation(explicit conic co) +{/*<asyxml></code><documentation>Return the coefficients of the equation of conic section in its coordinate system: + bqe.a[0] * x^2 + bqe.a[1] * x * y + bqe.a[2] * y^2 + bqe.a[3] * x + bqe.a[4] * y + bqe.a[5] = 0. + One can change the coordinate system of 'bqe' using the routine 'changecoordsys'.</documentation></function></asyxml>*/ + bqe obqe; + if(co.e == 0) + obqe = equation((circle)co); + else + if(co.e < 1) obqe = equation((ellipse)co); + else + if(co.e == 1) obqe = equation((parabola)co); + else + if(co.e > 1) obqe = equation((hyperbola)co); + else abort("draw: unknown conic."); + return obqe; +} + +/*<asyxml><function type="string" signature="conictype(bqe)"><code></asyxml>*/ +string conictype(bqe bqe) +{/*<asyxml></code><documentation>Returned values are "ellipse" or "parabola" or "hyperbola" + depending of the conic section represented by 'bqe'.</documentation></function></asyxml>*/ + bqe lbqe = changecoordsys(defaultcoordsys, bqe); + string os = "degenerated"; + real a = lbqe.a[0], b = lbqe.a[1]/2, c = lbqe.a[2], d = lbqe.a[3]/2, f = lbqe.a[4]/2, g = lbqe.a[5]; + real delta = a * c * g + b * f * d + d * b * f - (b^2 * g + d^2 * c + f^2 * a); + if(abs(delta) < 10 * epsgeo) return os; + real J = a * c - b^2; + real I = a + c; + if(J > epsgeo) { + if(delta/I < -epsgeo); + os = "ellipse"; + } else { + if(abs(J) < epsgeo) os = "parabola"; else os = "hyperbola"; + } + return os; +} + +/*<asyxml><function type="conic" signature="conic(point,point,point,point,point)"><code></asyxml>*/ +conic conic(point M1, point M2, point M3, point M4, point M5) +{/*<asyxml></code><documentation>Return the conic passing through 'M1', 'M2', 'M3', 'M4' and 'M5' if the conic is not degenerated.</documentation></function></asyxml>*/ + bqe bqe = bqe(M1, M2, M3, M4, M5); + string ct = conictype(bqe); + if(ct == "degenerated") abort("conic: degenerated conic passing through five points."); + if(ct == "ellipse") return ellipse(bqe); + if(ct == "parabola") return parabola(bqe); + return hyperbola(bqe); +} + +/*<asyxml><function type="coordsys" signature="canonicalcartesiansystem(hyperbola)"><code></asyxml>*/ +coordsys canonicalcartesiansystem(explicit conic co) +{/*<asyxml></code><documentation>Return the canonical cartesian system of the conic 'co'.</documentation></function></asyxml>*/ + if(co.e < 1) return canonicalcartesiansystem((ellipse)co); + else if(co.e == 1) return canonicalcartesiansystem((parabola)co); + return canonicalcartesiansystem((hyperbola)co); +} + +/*<asyxml><function type="bqe" signature="canonical(bqe)"><code></asyxml>*/ +bqe canonical(bqe bqe) +{/*<asyxml></code><documentation>Return the bivariate quadratic equation relative to the + canonical coordinate system of the conic section represented by 'bqe'.</documentation></function></asyxml>*/ + string type = conictype(bqe); + if(type == "") abort("canonical: the equation can not be performed."); + bqe obqe; + if(type == "ellipse") { + ellipse el = ellipse(bqe); + obqe = changecoordsys(canonicalcartesiansystem(el), equation(el)); + } else { + if(type == "parabola") { + parabola p = parabola(bqe); + obqe = changecoordsys(canonicalcartesiansystem(p), equation(p)); + } else { + hyperbola h = hyperbola(bqe); + obqe = changecoordsys(canonicalcartesiansystem(h), equation(h)); + } + } + return obqe; +} + +/*<asyxml><function type="conic" signature="conic(bqe)"><code></asyxml>*/ +conic conic(bqe bqe) +{/*<asyxml></code><documentation>Return the conic section represented by the bivariate quartic equation 'bqe'.</documentation></function></asyxml>*/ + string type = conictype(bqe); + if(type == "") abort("canonical: the equation can not be performed."); + conic oc; + if(type == "ellipse") { + oc = ellipse(bqe); + } else { + if(type == "parabola") oc = parabola(bqe); else oc = hyperbola(bqe); + } + return oc; +} + +/*<asyxml><function type="real" signature="arclength(circle)"><code></asyxml>*/ +real arclength(circle c) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return c.r * 2 * pi; +} + +/*<asyxml><function type="real" signature="focusToCenter(ellipse,real)"><code></asyxml>*/ +real focusToCenter(ellipse el, real a) +{/*<asyxml></code><documentation>Return the angle relatively to the center of 'el' for the angle 'a' + given relatively to the focus of 'el'.</documentation></function></asyxml>*/ + pair p = point(fromFocus(el, a, a, 1, CCW), 0); + pair c = locate(el.C); + real d = degrees(p - c) - el.angle; + d = abs(d) < epsgeo ? 0 : d; // Avoid -1e-15 + return d%(sgnd(a) * 360); +} + +/*<asyxml><function type="real" signature="centerToFocus(ellipse,real)"><code></asyxml>*/ +real centerToFocus(ellipse el, real a) +{/*<asyxml></code><documentation>Return the angle relatively to the focus of 'el' for the angle 'a' + given relatively to the center of 'el'.</documentation></function></asyxml>*/ + pair P = point(fromCenter(el, a, a, 1, CCW), 0); + pair F1 = locate(el.F1); + pair F2 = locate(el.F2); + real d = degrees(P - F1) - degrees(F2 - F1); + d = abs(d) < epsgeo ? 0 : d; // Avoid -1e-15 + return d%(sgnd(a) * 360); +} + +/*<asyxml><function type="real" signature="arclength(ellipse)"><code></asyxml>*/ +real arclength(ellipse el) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return degenerate(el) ? infinity : 4 * el.a * elle(pi/2, el.e); +} + +/*<asyxml><function type="real" signature="arclength(ellipse,real,real,bool,polarconicroutine)"><code></asyxml>*/ +real arclength(ellipse el, real angle1, real angle2, + bool direction = CCW, + polarconicroutine polarconicroutine = currentpolarconicroutine) +{/*<asyxml></code><documentation>Return the length of the arc of the ellipse between 'angle1' + and 'angle2'. + 'angle1' and 'angle2' must be in the interval ]-360;+oo[ if polarconicroutine = fromFocus, + ]-oo;+oo[ if polarconicroutine = fromCenter.</documentation></function></asyxml>*/ + if(degenerate(el)) return infinity; + if(angle1 > angle2) return arclength(el, angle2, angle1, !direction, polarconicroutine); + // path g;int n = 1000; + // if(el.e == 0) g = arcfromcenter(el, angle1, angle2, n, direction); + // if(el.e != 1) g = polarconicroutine(el, angle1, angle2, n, direction); + // write("with path = ", arclength(g)); + if(polarconicroutine == fromFocus) { + // dot(point(fromFocus(el, angle1, angle1, 1, CCW), 0), 2mm + blue); + // dot(point(fromFocus(el, angle2, angle2, 1, CCW), 0), 2mm + blue); + // write("fromfocus1 = ", angle1); + // write("fromfocus2 = ", angle2); + real gle1 = focusToCenter(el, angle1); + real gle2 = focusToCenter(el, angle2); + if((gle1 - gle2) * (angle1 - angle2) > 0) { + angle1 = gle1; angle2 = gle2; + } else { + angle1 = gle2; angle2 = gle1; + } + // dot(point(fromCenter(el, angle1, angle1, 1, CCW), 0), 1mm + red); + // dot(point(fromCenter(el, angle2, angle2, 1, CCW), 0), 1mm + red); + // write("fromcenter1 = ", angle1); + // write("fromcenter2 = ", angle2); + } + if(angle1 < 0 || angle2 < 0) return arclength(el, 180 + angle1, 180 + angle2, direction, fromCenter); + real a1 = direction ? angle1 : angle2; + real a2 = direction ? angle2 : angle1 + 360; + real elleq = el.a * elle(pi/2, el.e); + real S(real a) + {//Return the arclength from 0 to the angle 'a' (in degrees) + // given form the center of the ellipse. + real gle = atan(el.a * Tan(a)/el.b)+ + pi * (((a%90 == 0 && a != 0) ? floor(a/90) - 1 : floor(a/90)) - + ((a%180 == 0) ? 0 : floor(a/180)) - + (a%360 == 0 ? floor(a/(360)) : 0)); + /* // Uncomment to visualize the used branches + unitsize(2cm, 1cm); + import graph; + + real xmin = 0, xmax = 3pi; + + xlimits( xmin, xmax); + ylimits( 0, 10); + yaxis( "y" , LeftRight(), RightTicks(pTick=.8red, ptick = lightgrey, extend = true)); + xaxis( "x - value", BottomTop(), Ticks(Label("$%.2f$", red), Step = pi/2, step = pi/4, pTick=.8red, ptick = lightgrey, extend = true)); + + real p2 = pi/2; + real f(real t) + { + return atan(0.6 * tan(t))+ + pi * ((t%p2 == 0 && t != 0) ? floor(t/p2) - 1 : floor(t/p2)) - + ((t%pi == 0) ? 0 : pi * floor(t/pi)) - (t%(2pi) == 0 ? pi * floor(t/(2 * pi)) : 0); + } + + draw(graph(f, xmin, xmax, 100)); + write(degrees(f(pi/2))); + write(degrees(f(pi))); + write(degrees(f(3pi/2))); + write(degrees(f(2pi))); + draw(graph(new real(real t){return t;}, xmin, xmax, 3)); + */ + return elleq - el.a * elle(pi/2 - gle, el.e); + } + return S(a2) - S(a1); +} + +/*<asyxml><function type="real" signature="arclength(parabola,real)"><code></asyxml>*/ +real arclength(parabola p, real angle) +{/*<asyxml></code><documentation>Return the arclength from 180 to 'angle' given from focus in the + canonical coordinate system of 'p'.</documentation></function></asyxml>*/ + real a = p.a; /* In canonicalcartesiansystem(p) the equation of p + is x = y^2/(4a) */ + // integrate(sqrt(1 + (x/(2 * a))^2), x); + real S(real t){return 0.5 * t * sqrt(1 + t^2/(4 * a^2)) + a * asinh(t/(2 * a));} + real R(real gle){return 2 * a/(1 - Cos(gle));} + real t = Sin(angle) * R(angle); + return S(t); +} + +/*<asyxml><function type="real" signature="arclength(parabola,real,real)"><code></asyxml>*/ +real arclength(parabola p, real angle1, real angle2) +{/*<asyxml></code><documentation>Return the arclength from 'angle1' to 'angle2' given from + focus in the canonical coordinate system of 'p'</documentation></function></asyxml>*/ + return arclength(p, angle1) - arclength(p, angle2); +} + +/*<asyxml><function type="real" signature="arclength(parabola p)"><code></asyxml>*/ +real arclength(parabola p) +{/*<asyxml></code><documentation>Return the length of the arc of the parabola bounded to the bounding + box of the current picture.</documentation></function></asyxml>*/ + real[] b = bangles(p); + return arclength(p, b[0], b[1]); +} +// *........................CONICS.........................* +// *=======================================================* + +// *=======================================================* +// *.......................ABSCISSA........................* +/*<asyxml><struct signature="abscissa"><code></asyxml>*/ +struct abscissa +{/*<asyxml></code><documentation>Provide abscissa structure on a curve used in the routine-like 'point(object, abscissa)' + where object can be 'line','segment','ellipse','circle','conic'...</documentation><property type = "real" signature="x"><code></asyxml>*/ + real x;/*<asyxml></code><documentation>The abscissa value.</documentation></property><property type = "int" signature="system"><code></asyxml>*/ + int system;/*<asyxml></code><documentation>0 = relativesystem; 1 = curvilinearsystem; 2 = angularsystem; 3 = nodesystem</documentation></property><property type = "polarconicroutine" signature="polarconicroutine"><code></asyxml>*/ + polarconicroutine polarconicroutine = fromCenter;/*<asyxml></code><documentation>The routine used with angular system and two foci conic section. + Possible values are 'formCenter' and 'formFocus'.</documentation></property></asyxml>*/ + /*<asyxml><method type = "abscissa" signature="copy()"><code></asyxml>*/ + abscissa copy() + {/*<asyxml></code><documentation>Return a copy of this abscissa.</documentation></method></asyxml>*/ + abscissa oa = new abscissa; + oa.x = this.x; + oa.system = this.system; + oa.polarconicroutine = this.polarconicroutine; + return oa; + } +}/*<asyxml></struct></asyxml>*/ + +/*<asyxml><constant type = "int" signature="relativesystem,curvilinearsystem,angularsystem,nodesystem"><code></asyxml>*/ +restricted int relativesystem = 0, curvilinearsystem = 1, angularsystem = 2, nodesystem = 3;/*<asyxml></code><documentation>Constant used to set the abscissa system.</documentation></constant></asyxml>*/ + +/*<asyxml><operator type = "abscissa" signature="cast(explicit position)"><code></asyxml>*/ +abscissa operator cast(explicit position position) +{/*<asyxml></code><documentation>Cast position to abscissa. + If 'position' is relative, the abscissa is relative else it's a curvilinear abscissa.</documentation></operator></asyxml>*/ + abscissa oarcc; + oarcc.x = position.position.x; + oarcc.system = position.relative ? relativesystem : curvilinearsystem; + return oarcc; +} + +/*<asyxml><operator type = "abscissa" signature="+(real,explicit abscissa)"><code></asyxml>*/ +abscissa operator +(real x, explicit abscissa a) +{/*<asyxml></code><documentation>Provide 'real + abscissa'. + Return abscissa b so that b.x = a.x + x. + +(explicit abscissa, real), -(real, explicit abscissa) and -(explicit abscissa, real) are also defined.</documentation></operator></asyxml>*/ + abscissa oa = a.copy(); + oa.x = a.x + x; + return oa; +} + +abscissa operator +(explicit abscissa a, real x) +{ + return x + a; +} +abscissa operator +(int x, explicit abscissa a) +{ + return ((real)x) + a; +} + +/*<asyxml><operator type = "abscissa" signature="-(explicit abscissa a)"><code></asyxml>*/ +abscissa operator -(explicit abscissa a) +{/*<asyxml></code><documentation>Return the abscissa b so that b.x = -a.x.</documentation></operator></asyxml>*/ + abscissa oa; + oa.system = a.system; + oa.x = -a.x; + return oa; +} + +abscissa operator -(real x, explicit abscissa a) +{ + abscissa oa; + oa.system = a.system; + oa.x = x - a.x; + return oa; +} +abscissa operator -(explicit abscissa a, real x) +{ + abscissa oa; + oa.system = a.system; + oa.x = a.x - x; + return oa; +} +abscissa operator -(int x, explicit abscissa a) +{ + return ((real)x) - a; +} + +/*<asyxml><operator type = "abscissa" signature="*(real,abscissa)"><code></asyxml>*/ +abscissa operator *(real x, explicit abscissa a) +{/*<asyxml></code><documentation>Provide 'real * abscissa'. + Return abscissa b so that b.x = x * a.x. + *(explicit abscissa, real), /(real, explicit abscissa) and /(explicit abscissa, real) are also defined.</documentation></operator></asyxml>*/ + abscissa oa; + oa.system = a.system; + oa.x = a.x * x; + return oa; +} +abscissa operator *(explicit abscissa a, real x) +{ + return x * a; +} + +abscissa operator /(real x, explicit abscissa a) +{ + abscissa oa; + oa.system = a.system; + oa.x = x/a.x; + return oa; +} +abscissa operator /(explicit abscissa a, real x) +{ + abscissa oa; + oa.system = a.system; + oa.x = a.x/x; + return oa; +} + +abscissa operator /(int x, explicit abscissa a) +{ + return ((real)x)/a; +} + +/*<asyxml><function type="abscissa" signature="relabscissa(real)"><code></asyxml>*/ +abscissa relabscissa(real x) +{/*<asyxml></code><documentation>Return a relative abscissa.</documentation></function></asyxml>*/ + return (abscissa)(Relative(x)); +} +abscissa relabscissa(int x) +{ + return (abscissa)(Relative(x)); +} + +/*<asyxml><function type="abscissa" signature="curabscissa(real)"><code></asyxml>*/ +abscissa curabscissa(real x) +{/*<asyxml></code><documentation>Return a curvilinear abscissa.</documentation></function></asyxml>*/ + return (abscissa)((position)x); +} +abscissa curabscissa(int x) +{ + return (abscissa)((position)x); +} + +/*<asyxml><function type="abscissa" signature="angabscissa(real,polarconicroutine)"><code></asyxml>*/ +abscissa angabscissa(real x, polarconicroutine polarconicroutine = currentpolarconicroutine) +{/*<asyxml></code><documentation>Return a angular abscissa.</documentation></function></asyxml>*/ + abscissa oarcc; + oarcc.x = x; + oarcc.polarconicroutine = polarconicroutine; + oarcc.system = angularsystem; + return oarcc; +} +abscissa angabscissa(int x, polarconicroutine polarconicroutine = currentpolarconicroutine) +{ + return angabscissa((real)x, polarconicroutine); +} + +/*<asyxml><function type="abscissa" signature="nodabscissa(real)"><code></asyxml>*/ +abscissa nodabscissa(real x) +{/*<asyxml></code><documentation>Return an abscissa as time on the path.</documentation></function></asyxml>*/ + abscissa oarcc; + oarcc.x = x; + oarcc.system = nodesystem; + return oarcc; +} +abscissa nodabscissa(int x) +{ + return nodabscissa((real)x); +} + +/*<asyxml><operator type = "abscissa" signature="cast(real)"><code></asyxml>*/ +abscissa operator cast(real x) +{/*<asyxml></code><documentation>Cast real to abscissa, precisely 'nodabscissa'.</documentation></operator></asyxml>*/ + return nodabscissa(x); +} +abscissa operator cast(int x) +{ + return nodabscissa((real)x); +} + +/*<asyxml><function type="point" signature="point(circle,abscissa)"><code></asyxml>*/ +point point(circle c, abscissa l) +{/*<asyxml></code><documentation>Return the point of 'c' which has the abscissa 'l.x' + according to the abscissa system 'l.system'.</documentation></function></asyxml>*/ + coordsys R = c.C.coordsys; + if (l.system == nodesystem) + return point(R, point((path)c, l.x)/R); + if (l.system == relativesystem) + return c.C + point(R, R.polar(c.r, 2 * pi * l.x)); + if (l.system == curvilinearsystem) + return c.C + point(R, R.polar(c.r, l.x/c.r)); + if (l.system == angularsystem) + return c.C + point(R, R.polar(c.r, radians(l.x))); + abort("point: bad abscissa system."); + return (0, 0); +} + +/*<asyxml><function type="point" signature="point(ellipse,abscissa)"><code></asyxml>*/ +point point(ellipse el, abscissa l) +{/*<asyxml></code><documentation>Return the point of 'el' which has the abscissa 'l.x' + according to the abscissa system 'l.system'.</documentation></function></asyxml>*/ + if(el.e == 0) return point((circle)el, l); + coordsys R = coordsys(el); + if (l.system == nodesystem) + return point(R, point((path)el, l.x)/R); + if (l.system == relativesystem) { + return point(el, curabscissa((l.x%1) * arclength(el))); + } + if (l.system == curvilinearsystem) { + real a1 = 0, a2 = 360, cx = 0; + real aout = a1; + real x = abs(l.x)%arclength(el); + while (abs(cx - x) > epsgeo) { + aout = (a1 + a2)/2; + cx = arclength(el, 0, aout, CCW, fromCenter); //fromCenter is speeder + if(cx > x) a2 = (a1 + a2)/2; else a1 = (a1 + a2)/2; + } + path pel = fromCenter(el, sgn(l.x) * aout, sgn(l.x) * aout, 1, CCW); + return point(R, point(pel, 0)/R); + } + if (l.system == angularsystem) { + return point(R, point(l.polarconicroutine(el, l.x, l.x, 1, CCW), 0)/R); + } + abort("point: bad abscissa system."); + return (0, 0); +} + +/*<asyxml><function type="point" signature="point(parabola,abscissa)"><code></asyxml>*/ +point point(parabola p, abscissa l) +{/*<asyxml></code><documentation>Return the point of 'p' which has the abscissa 'l.x' + according to the abscissa system 'l.system'.</documentation></function></asyxml>*/ + coordsys R = coordsys(p); + if (l.system == nodesystem) + return point(R, point((path)p, l.x)/R); + if (l.system == relativesystem) { + real[] b = bangles(p); + real al = sgn(l.x) > 0 ? arclength(p, 180, b[1]) : arclength(p, 180, b[0]); + return point(p, curabscissa(abs(l.x) * al)); + } + if (l.system == curvilinearsystem) { + real a1 = 1e-3, a2 = 360 - 1e-3, cx = infinity; + while (abs(cx - l.x) > epsgeo) { + cx = arclength(p, 180, (a1 + a2)/2); + if(cx > l.x) a2 = (a1 + a2)/2; else a1 = (a1 + a2)/2; + } + path pp = fromFocus(p, a1, a1, 1, CCW); + return point(R, point(pp, 0)/R); + } + if (l.system == angularsystem) { + return point(R, point(fromFocus(p, l.x, l.x, 1, CCW), 0)/R); + } + abort("point: bad abscissa system."); + return (0, 0); +} + +/*<asyxml><function type="point" signature="point(hyperbola,abscissa)"><code></asyxml>*/ +point point(hyperbola h, abscissa l) +{/*<asyxml></code><documentation>Return the point of 'h' which has the abscissa 'l.x' + according to the abscissa system 'l.system'.</documentation></function></asyxml>*/ + coordsys R = coordsys(h); + if (l.system == nodesystem) + return point(R, point((path)h, l.x)/R); + if (l.system == relativesystem) { + abort("point(hyperbola, relativeSystem) is not implemented... +Try relpoint((path)your_hyperbola, x);"); + } + if (l.system == curvilinearsystem) { + abort("point(hyperbola, curvilinearSystem) is not implemented..."); + } + if (l.system == angularsystem) { + return point(R, point(l.polarconicroutine(h, l.x, l.x, 1, CCW), 0)/R); + } + abort("point: bad abscissa system."); + return (0, 0); +} + +/*<asyxml><function type="abscissa" signature="point(conic,point)"><code></asyxml>*/ +point point(explicit conic co, abscissa l) +{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the conic 'co'.</documentation></function></asyxml>*/ + if(co.e == 0) return point((circle)co, l); + if(co.e < 1) return point((ellipse)co, l); + if(co.e == 1) return point((parabola)co, l); + return point((hyperbola)co, l); +} + + +/*<asyxml><function type="point" signature="point(line,abscissa)"><code></asyxml>*/ +point point(line l, abscissa x) +{/*<asyxml></code><documentation>Return the point of 'l' which has the abscissa 'l.x' according to the abscissa system 'l.system'. + Note that the origin is l.A, and point(l, relabscissa(x)) returns l.A + x.x * vector(l.B - l.A).</documentation></function></asyxml>*/ + coordsys R = l.A.coordsys; + if (x.system == nodesystem) + return l.A + (x.x < 0 ? 0 : x.x > 1 ? 1 : x.x) * vector(l.B - l.A); + if (x.system == relativesystem) + return l.A + x.x * vector(l.B - l.A); + if (x.system == curvilinearsystem) + return l.A + x.x * l.u; + if (x.system == angularsystem) + abort("point: what the meaning of angular abscissa on line ?."); + abort("point: bad abscissa system."); + return (0, 0); +} + +/*<asyxml><function type="point" signature="point(line,real)"><code></asyxml>*/ +point point(line l, explicit real x) +{/*<asyxml></code><documentation>Return the point between node l.A and l.B (x <= 0 means l.A, x >=1 means l.B).</documentation></function></asyxml>*/ + return point(l, nodabscissa(x)); +} +point point(line l, explicit int x) +{ + return point(l, nodabscissa(x)); +} + +/*<asyxml><function type="circle" signature="point(explicit circle,explicit real)"><code></asyxml>*/ +point point(explicit circle c, explicit real x) +{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x) + 1.</documentation></function></asyxml>*/ + return point(c, nodabscissa(x)); +} +point point(explicit circle c, explicit int x) +{ + return point(c, nodabscissa(x)); +} + +/*<asyxml><function type="point" signature="point(explicit ellipse,explicit real)"><code></asyxml>*/ +point point(explicit ellipse el, explicit real x) +{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x) + 1.</documentation></function></asyxml>*/ + return point(el, nodabscissa(x)); +} +point point(explicit ellipse el, explicit int x) +{ + return point(el, nodabscissa(x)); +} + +/*<asyxml><function type="point" signature="point(explicit parabola,explicit real)"><code></asyxml>*/ +point point(explicit parabola p, explicit real x) +{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x) + 1.</documentation></function></asyxml>*/ + return point(p, nodabscissa(x)); +} +point point(explicit parabola p, explicit int x) +{ + return point(p, nodabscissa(x)); +} + +/*<asyxml><function type="point" signature="point(explicit hyperbola,explicit real)"><code></asyxml>*/ +point point(explicit hyperbola h, explicit real x) +{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x) + 1.</documentation></function></asyxml>*/ + return point(h, nodabscissa(x)); +} +point point(explicit hyperbola h, explicit int x) +{ + return point(h, nodabscissa(x)); +} + +/*<asyxml><function type="point" signature="point(explicit conic,explicit real)"><code></asyxml>*/ +point point(explicit conic co, explicit real x) +{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x) + 1.</documentation></function></asyxml>*/ + point op; + if(co.e == 0) op = point((circle)co, nodabscissa(x)); + else if(co.e < 1) op = point((ellipse)co, nodabscissa(x)); + else if(co.e == 1) op = point((parabola)co, nodabscissa(x)); + else op = point((hyperbola)co, nodabscissa(x)); + return op; +} +point point(explicit conic co, explicit int x) +{ + return point(co, (real)x); +} + +/*<asyxml><function type="point" signature="relpoint(line,real)"><code></asyxml>*/ +point relpoint(line l, real x) +{/*<asyxml></code><documentation>Return the relative point of 'l' (0 means l.A, + 1 means l.B, x means l.A + x * vector(l.B - l.A) ).</documentation></function></asyxml>*/ + return point(l, Relative(x)); +} + +/*<asyxml><function type="point" signature="relpoint(explicit circle,real)"><code></asyxml>*/ +point relpoint(explicit circle c, real x) +{/*<asyxml></code><documentation>Return the relative point of 'c' (0 means origin, 1 means end). + Origin is c.center + c.r * (1, 0).</documentation></function></asyxml>*/ + return point(c, Relative(x)); +} + +/*<asyxml><function type="point" signature="relpoint(explicit ellipse,real)"><code></asyxml>*/ +point relpoint(explicit ellipse el, real x) +{/*<asyxml></code><documentation>Return the relative point of 'el' (0 means origin, 1 means end).</documentation></function></asyxml>*/ + return point(el, Relative(x)); +} + +/*<asyxml><function type="point" signature="relpoint(explicit parabola,real)"><code></asyxml>*/ +point relpoint(explicit parabola p, real x) +{/*<asyxml></code><documentation>Return the relative point of the path of the parabola + bounded by the bounding box of the current picture. + 0 means origin, 1 means end, where the origin is the vertex of 'p'.</documentation></function></asyxml>*/ + return point(p, Relative(x)); +} + +/*<asyxml><function type="point" signature="relpoint(explicit hyperbola,real)"><code></asyxml>*/ +point relpoint(explicit hyperbola h, real x) +{/*<asyxml></code><documentation>Not yet implemented... <look href = "point(hyperbola, abscissa)"/></documentation></function></asyxml>*/ + return point(h, Relative(x)); +} + +/*<asyxml><function type="point" signature="relpoint(explicit conic,explicit real)"><code></asyxml>*/ +point relpoint(explicit conic co, explicit real x) +{/*<asyxml></code><documentation>Return the relative point of 'co' (0 means origin, 1 means end).</documentation></function></asyxml>*/ + point op; + if(co.e == 0) op = point((circle)co, Relative(x)); + else if(co.e < 1) op = point((ellipse)co, Relative(x)); + else if(co.e == 1) op = point((parabola)co, Relative(x)); + else op = point((hyperbola)co, Relative(x)); + return op; +} +point relpoint(explicit conic co, explicit int x) +{ + return relpoint(co, (real)x); +} + +/*<asyxml><function type="point" signature="angpoint(explicit circle,real)"><code></asyxml>*/ +point angpoint(explicit circle c, real x) +{/*<asyxml></code><documentation>Return the point of 'c' in the direction 'x' measured in degrees.</documentation></function></asyxml>*/ + return point(c, angabscissa(x)); +} + +/*<asyxml><function type="point" signature="angpoint(explicit ellipse,real,polarconicroutine)"><code></asyxml>*/ +point angpoint(explicit ellipse el, real x, + polarconicroutine polarconicroutine = currentpolarconicroutine) +{/*<asyxml></code><documentation>Return the point of 'el' in the direction 'x' + measured in degrees according to 'polarconicroutine'.</documentation></function></asyxml>*/ + return el.e == 0 ? angpoint((circle) el, x) : point(el, angabscissa(x, polarconicroutine)); +} + +/*<asyxml><function type="point" signature="angpoint(explicit parabola,real)"><code></asyxml>*/ +point angpoint(explicit parabola p, real x) +{/*<asyxml></code><documentation>Return the point of 'p' in the direction 'x' measured in degrees.</documentation></function></asyxml>*/ + return point(p, angabscissa(x)); +} + +/*<asyxml><function type="point" signature="angpoint(explicit hyperbola,real,polarconicroutine)"><code></asyxml>*/ +point angpoint(explicit hyperbola h, real x, + polarconicroutine polarconicroutine = currentpolarconicroutine) +{/*<asyxml></code><documentation>Return the point of 'h' in the direction 'x' + measured in degrees according to 'polarconicroutine'.</documentation></function></asyxml>*/ + return point(h, angabscissa(x, polarconicroutine)); +} + +/*<asyxml><function type="point" signature="curpoint(line,real)"><code></asyxml>*/ +point curpoint(line l, real x) +{/*<asyxml></code><documentation>Return the point of 'l' which has the curvilinear abscissa 'x'. + Origin is l.A.</documentation></function></asyxml>*/ + return point(l, curabscissa(x)); +} + +/*<asyxml><function type="point" signature="curpoint(explicit circle,real)"><code></asyxml>*/ +point curpoint(explicit circle c, real x) +{/*<asyxml></code><documentation>Return the point of 'c' which has the curvilinear abscissa 'x'. + Origin is c.center + c.r * (1, 0).</documentation></function></asyxml>*/ + return point(c, curabscissa(x)); +} + +/*<asyxml><function type="point" signature="curpoint(explicit ellipse,real)"><code></asyxml>*/ +point curpoint(explicit ellipse el, real x) +{/*<asyxml></code><documentation>Return the point of 'el' which has the curvilinear abscissa 'el'.</documentation></function></asyxml>*/ + return point(el, curabscissa(x)); +} + +/*<asyxml><function type="point" signature="curpoint(explicit parabola,real)"><code></asyxml>*/ +point curpoint(explicit parabola p, real x) +{/*<asyxml></code><documentation>Return the point of 'p' which has the curvilinear abscissa 'x'. + Origin is the vertex of 'p'.</documentation></function></asyxml>*/ + return point(p, curabscissa(x)); +} + +/*<asyxml><function type="point" signature="curpoint(conic,real)"><code></asyxml>*/ +point curpoint(conic co, real x) +{/*<asyxml></code><documentation>Return the point of 'co' which has the curvilinear abscissa 'x'.</documentation></function></asyxml>*/ + point op; + if(co.e == 0) op = point((circle)co, curabscissa(x)); + else if(co.e < 1) op = point((ellipse)co, curabscissa(x)); + else if(co.e == 1) op = point((parabola)co, curabscissa(x)); + else op = point((hyperbola)co, curabscissa(x)); + return op; +} + +/*<asyxml><function type="abscissa" signature="angabscissa(circle,point)"><code></asyxml>*/ +abscissa angabscissa(circle c, point M) +{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the circle 'c'.</documentation></function></asyxml>*/ + if(!(M @ c)) abort("angabscissa: the point is not on the circle."); + abscissa oa; + oa.system = angularsystem; + oa.x = degrees(M - c.C); + if(oa.x < 0) oa.x+=360; + return oa; +} + +/*<asyxml><function type="abscissa" signature="angabscissa(ellipse,point,polarconicroutine)"><code></asyxml>*/ +abscissa angabscissa(ellipse el, point M, + polarconicroutine polarconicroutine = currentpolarconicroutine) +{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the ellipse 'el' according to 'polarconicroutine'.</documentation></function></asyxml>*/ + if(!(M @ el)) abort("angabscissa: the point is not on the ellipse."); + abscissa oa; + oa.system = angularsystem; + oa.polarconicroutine = polarconicroutine; + oa.x = polarconicroutine == fromCenter ? degrees(M - el.C) : degrees(M - el.F1); + oa.x -= el.angle; + if(oa.x < 0) oa.x += 360; + return oa; +} + +/*<asyxml><function type="abscissa" signature="angabscissa(hyperbola,point,polarconicroutine)"><code></asyxml>*/ +abscissa angabscissa(hyperbola h, point M, + polarconicroutine polarconicroutine = currentpolarconicroutine) +{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the hyperbola 'h' according to 'polarconicroutine'.</documentation></function></asyxml>*/ + if(!(M @ h)) abort("angabscissa: the point is not on the hyperbola."); + abscissa oa; + oa.system = angularsystem; + oa.polarconicroutine = polarconicroutine; + oa.x = polarconicroutine == fromCenter ? degrees(M - h.C) : degrees(M - h.F1) + 180; + oa.x -= h.angle; + if(oa.x < 0) oa.x += 360; + return oa; +} + +/*<asyxml><function type="abscissa" signature="angabscissa(parabola,point)"><code></asyxml>*/ +abscissa angabscissa(parabola p, point M) +{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the parabola 'p'.</documentation></function></asyxml>*/ + if(!(M @ p)) abort("angabscissa: the point is not on the parabola."); + abscissa oa; + oa.system = angularsystem; + oa.polarconicroutine = fromFocus;// Not used + oa.x = degrees(M - p.F); + oa.x -= p.angle; + if(oa.x < 0) oa.x += 360; + return oa; +} + +/*<asyxml><function type="abscissa" signature="angabscissa(conic,point)"><code></asyxml>*/ +abscissa angabscissa(explicit conic co, point M) +{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the conic 'co'.</documentation></function></asyxml>*/ + if(co.e == 0) return angabscissa((circle)co, M); + if(co.e < 1) return angabscissa((ellipse)co, M); + if(co.e == 1) return angabscissa((parabola)co, M); + return angabscissa((hyperbola)co, M); +} + +/*<asyxml><function type="abscissa" signature="curabscissa(line,point)"><code></asyxml>*/ +abscissa curabscissa(line l, point M) +{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the line 'l'.</documentation></function></asyxml>*/ + if(!(M @ extend(l))) abort("curabscissa: the point is not on the line."); + abscissa oa; + oa.system = curvilinearsystem; + oa.x = sgn(dot(M - l.A, l.B - l.A)) * abs(M - l.A); + return oa; +} + +/*<asyxml><function type="abscissa" signature="curabscissa(circle,point)"><code></asyxml>*/ +abscissa curabscissa(circle c, point M) +{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the circle 'c'.</documentation></function></asyxml>*/ + if(!(M @ c)) abort("curabscissa: the point is not on the circle."); + abscissa oa; + oa.system = curvilinearsystem; + oa.x = pi * angabscissa(c, M).x * c.r/180; + return oa; +} + +/*<asyxml><function type="abscissa" signature="curabscissa(ellipse,point)"><code></asyxml>*/ +abscissa curabscissa(ellipse el, point M) +{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the ellipse 'el'.</documentation></function></asyxml>*/ + if(!(M @ el)) abort("curabscissa: the point is not on the ellipse."); + abscissa oa; + oa.system = curvilinearsystem; + real a = angabscissa(el, M, fromCenter).x; + oa.x = arclength(el, 0, a, fromCenter); + oa.polarconicroutine = fromCenter; + return oa; +} + +/*<asyxml><function type="abscissa" signature="curabscissa(parabola,point)"><code></asyxml>*/ +abscissa curabscissa(parabola p, point M) +{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the parabola 'p'.</documentation></function></asyxml>*/ + if(!(M @ p)) abort("curabscissa: the point is not on the parabola."); + abscissa oa; + oa.system = curvilinearsystem; + real a = angabscissa(p, M).x; + oa.x = arclength(p, 180, a); + oa.polarconicroutine = fromFocus; // Not used. + return oa; +} + +/*<asyxml><function type="abscissa" signature="curabscissa(conic,point)"><code></asyxml>*/ +abscissa curabscissa(conic co, point M) +{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the conic 'co'.</documentation></function></asyxml>*/ + if(co.e > 1) abort("curabscissa: not implemented for this hyperbola."); + if(co.e == 0) return curabscissa((circle)co, M); + if(co.e < 1) return curabscissa((ellipse)co, M); + return curabscissa((parabola)co, M); +} + +/*<asyxml><function type="abscissa" signature="nodabscissa(line,point)"><code></asyxml>*/ +abscissa nodabscissa(line l, point M) +{/*<asyxml></code><documentation>Return the node abscissa of 'M' on the line 'l'.</documentation></function></asyxml>*/ + if(!(M @ (segment)l)) abort("nodabscissa: the point is not on the segment."); + abscissa oa; + oa.system = nodesystem; + oa.x = abs(M - l.A)/abs(l.A - l.B); + return oa; +} + +/*<asyxml><function type="abscissa" signature="nodabscissa(circle,point)"><code></asyxml>*/ +abscissa nodabscissa(circle c, point M) +{/*<asyxml></code><documentation>Return the node abscissa of 'M' on the circle 'c'.</documentation></function></asyxml>*/ + if(!(M @ c)) abort("nodabscissa: the point is not on the circle."); + abscissa oa; + oa.system = nodesystem; + oa.x = intersect((path)c, locate(M))[0]; + return oa; +} + +/*<asyxml><function type="abscissa" signature="nodabscissa(ellipse,point)"><code></asyxml>*/ +abscissa nodabscissa(ellipse el, point M) +{/*<asyxml></code><documentation>Return the node abscissa of 'M' on the ellipse 'el'.</documentation></function></asyxml>*/ + if(!(M @ el)) abort("nodabscissa: the point is not on the ellipse."); + abscissa oa; + oa.system = nodesystem; + oa.x = intersect((path)el, M)[0]; + return oa; +} + +/*<asyxml><function type="abscissa" signature="nodabscissa(parabola,point)"><code></asyxml>*/ +abscissa nodabscissa(parabola p, point M) +{/*<asyxml></code><documentation>Return the node abscissa OF 'M' on the parabola 'p'.</documentation></function></asyxml>*/ + if(!(M @ p)) abort("nodabscissa: the point is not on the parabola."); + abscissa oa; + oa.system = nodesystem; + path pg = p; + real[] t = intersect(pg, M, 1e-5); + if(t.length == 0) abort("nodabscissa: the point is not on the path of the parabola."); + oa.x = t[0]; + return oa; +} + +/*<asyxml><function type="abscissa" signature="nodabscissa(conic,point)"><code></asyxml>*/ +abscissa nodabscissa(conic co, point M) +{/*<asyxml></code><documentation>Return the node abscissa of 'M' on the conic 'co'.</documentation></function></asyxml>*/ + if(co.e > 1) abort("nodabscissa: not implemented for hyperbola."); + if(co.e == 0) return nodabscissa((circle)co, M); + if(co.e < 1) return nodabscissa((ellipse)co, M); + return nodabscissa((parabola)co, M); +} + + +/*<asyxml><function type="abscissa" signature="relabscissa(line,point)"><code></asyxml>*/ +abscissa relabscissa(line l, point M) +{/*<asyxml></code><documentation>Return the relative abscissa of 'M' on the line 'l'.</documentation></function></asyxml>*/ + if(!(M @ extend(l))) abort("relabscissa: the point is not on the line."); + abscissa oa; + oa.system = relativesystem; + oa.x = sgn(dot(M - l.A, l.B - l.A)) * abs(M - l.A)/abs(l.A - l.B); + return oa; +} + +/*<asyxml><function type="abscissa" signature="relabscissa(circle,point)"><code></asyxml>*/ +abscissa relabscissa(circle c, point M) +{/*<asyxml></code><documentation>Return the relative abscissa of 'M' on the circle 'c'.</documentation></function></asyxml>*/ + if(!(M @ c)) abort("relabscissa: the point is not on the circle."); + abscissa oa; + oa.system = relativesystem; + oa.x = angabscissa(c, M).x/360; + return oa; +} + +/*<asyxml><function type="abscissa" signature="relabscissa(ellipse,point)"><code></asyxml>*/ +abscissa relabscissa(ellipse el, point M) +{/*<asyxml></code><documentation>Return the relative abscissa of 'M' on the ellipse 'el'.</documentation></function></asyxml>*/ + if(!(M @ el)) abort("relabscissa: the point is not on the ellipse."); + abscissa oa; + oa.system = relativesystem; + oa.x = curabscissa(el, M).x/arclength(el); + oa.polarconicroutine = fromFocus; + return oa; +} + +/*<asyxml><function type="abscissa" signature="relabscissa(conic,point)"><code></asyxml>*/ +abscissa relabscissa(conic co, point M) +{/*<asyxml></code><documentation>Return the relative abscissa of 'M' + on the conic 'co'.</documentation></function></asyxml>*/ + if(co.e > 1) abort("relabscissa: not implemented for hyperbola and parabola."); + if(co.e == 1) return relabscissa((parabola)co, M); + if(co.e == 0) return relabscissa((circle)co, M); + return relabscissa((ellipse)co, M); +} +// *.......................ABSCISSA........................* +// *=======================================================* + +// *=======================================================* +// *.........................ARCS..........................* +/*<asyxml><struct signature="arc"><code></asyxml>*/ +struct arc { + /*<asyxml></code><documentation>Implement oriented ellipse (included circle) arcs. + All the calculus with this structure will be as exact as Asymptote can do. + For a full precision, you must not cast 'arc' to 'path' excepted for drawing routines. + </documentation><property type = "ellipse" signature="el"><code></asyxml>*/ + ellipse el;/*<asyxml></code><documentation>The support of the arc.</documentation></property><property type = "real" signature="angle0"><code></asyxml>*/ + restricted real angle0 = 0;/*<asyxml></code><documentation>Internal use: rotating a circle does not modify the origin point,this variable stocks the eventual angle rotation. This value is not used for ellipses which are not circles.</documentation></property><property type = "real" signature="angle1,angle2"><code></asyxml>*/ + restricted real angle1, angle2;/*<asyxml></code><documentation>Values (in degrees) in ]-360, 360[.</documentation></property><property type = "bool" signature="direction"><code></asyxml>*/ + bool direction = CCW;/*<asyxml></code><documentation>The arc will be drawn from 'angle1' to 'angle2' rotating in the direction 'direction'.</documentation></property><property type = "polarconicroutine" signature="polarconicroutine"><code></asyxml>*/ + polarconicroutine polarconicroutine = currentpolarconicroutine;/*<asyxml></code><documentation>The routine to which the angles refer. + If 'el' is a circle 'fromCenter' is always used.</documentation></property></asyxml>*/ + + /*<asyxml><method type = "void" signature="setangles(real,real,real)"><code></asyxml>*/ + void setangles(real a0, real a1, real a2) + {/*<asyxml></code><documentation>Set the angles.</documentation></method></asyxml>*/ + if (a1 < 0 && a2 < 0) { + a1 += 360; + a2 += 360; + } + this.angle0 = a0%(sgnd(a0) * 360); + this.angle1 = a1%(sgnd(a1) * 360); + this.angle2 = a2%(sgnd(2) * 360); + } + + /*<asyxml><method type = "void" signature="init(ellipse,real,real,real,polarconicroutine,bool)"><code></asyxml>*/ + void init(ellipse el, real angle0 = 0, real angle1, real angle2, + polarconicroutine polarconicroutine, + bool direction = CCW) + {/*<asyxml></code><documentation>Constructor.</documentation></method></asyxml>*/ + if(abs(angle1 - angle2) > 360) abort("arc: |angle1 - angle2| > 360."); + this.el = el; + this.setangles(angle0, angle1, angle2); + this.polarconicroutine = polarconicroutine; + this.direction = direction; + } + + /*<asyxml><method type = "arc" signature="copy()"><code></asyxml>*/ + arc copy() + {/*<asyxml></code><documentation>Copy the arc.</documentation></method></asyxml>*/ + arc oa = new arc; + oa.el = this.el; + oa.direction = this.direction; + oa.polarconicroutine = this.polarconicroutine; + oa.angle1 = this.angle1; + oa.angle2 = this.angle2; + oa.angle0 = this.angle0; + return oa; + } +}/*<asyxml></struct></asyxml>*/ + +/*<asyxml><function type="polarconicroutine" signature="polarconicroutine(ellipse)"><code></asyxml>*/ +polarconicroutine polarconicroutine(conic co) +{/*<asyxml></code><documentation>Return the default routine used to draw a conic.</documentation></function></asyxml>*/ + if(co.e == 0) return fromCenter; + if(co.e == 1) return fromFocus; + return currentpolarconicroutine; +} + +/*<asyxml><function type="arc" signature="arc(ellipse,real,real,polarconicroutine,bool)"><code></asyxml>*/ +arc arc(ellipse el, real angle1, real angle2, + polarconicroutine polarconicroutine = polarconicroutine(el), + bool direction = CCW) +{/*<asyxml></code><documentation>Return the ellipse arc from 'angle1' to 'angle2' with respect to 'polarconicroutine' and rotating in the direction 'direction'.</documentation></function></asyxml>*/ + arc oa; + oa.init(el, 0, angle1, angle2, polarconicroutine, direction); + return oa; +} + +/*<asyxml><function type="arc" signature="complementary(arc)"><code></asyxml>*/ +arc complementary(arc a) +{/*<asyxml></code><documentation>Return the complementary of 'a'.</documentation></function></asyxml>*/ + arc oa; + oa.init(a.el, a.angle0, a.angle2, a.angle1, a.polarconicroutine, a.direction); + return oa; +} + +/*<asyxml><function type="arc" signature="reverse(arc)"><code></asyxml>*/ +arc reverse(arc a) +{/*<asyxml></code><documentation>Return arc 'a' oriented in reverse direction.</documentation></function></asyxml>*/ + arc oa; + oa.init(a.el, a.angle0, a.angle2, a.angle1, a.polarconicroutine, !a.direction); + return oa; +} + +/*<asyxml><function type="real" signature="degrees(arc)"><code></asyxml>*/ +real degrees(arc a) +{/*<asyxml></code><documentation>Return the measure in degrees of the oriented arc 'a'.</documentation></function></asyxml>*/ + real or; + real da = a.angle2 - a.angle1; + if(a.direction) { + or = a.angle1 < a.angle2 ? da : 360 + da; + } else { + or = a.angle1 < a.angle2 ? -360 + da : da; + } + return or; +} + +/*<asyxml><function type="real" signature="angle(a)"><code></asyxml>*/ +real angle(arc a) +{/*<asyxml></code><documentation>Return the measure in radians of the oriented arc 'a'.</documentation></function></asyxml>*/ + return radians(degrees(a)); +} + +/*<asyxml><function type="int" signature="arcnodesnumber(explicit arc)"><code></asyxml>*/ +int arcnodesnumber(explicit arc a) +{/*<asyxml></code><documentation>Return the number of nodes to draw the arc 'a'.</documentation></function></asyxml>*/ + return ellipsenodesnumber(a.el.a, a.el.b, a.angle1, a.angle2, a.direction); +} + +private path arctopath(arc a, int n) +{ + if(a.el.e == 0) return arcfromcenter(a.el, a.angle0 + a.angle1, a.angle0 + a.angle2, a.direction, n); + if(a.el.e != 1) return a.polarconicroutine(a.el, a.angle1, a.angle2, n, a.direction); + return arcfromfocus(a.el, a.angle1, a.angle2, n, a.direction); +} + +/*<asyxml><function type="point" signature="angpoint(arc,real)"><code></asyxml>*/ +point angpoint(arc a, real angle) +{/*<asyxml></code><documentation>Return the point given by its angular position (in degrees) relative to the arc 'a'. + If 'angle > degrees(a)' or 'angle < 0' the returned point is on the extended arc.</documentation></function></asyxml>*/ + pair p; + if(a.el.e == 0) { + real gle = a.angle0 + a.angle1 + (a.direction ? angle : -angle); + p = point(arcfromcenter(a.el, gle, gle, CCW, 1), 0); + } + else { + real gle = a.angle1 + (a.direction ? angle : -angle); + p = point(a.polarconicroutine(a.el, gle, gle, 1, CCW), 0); + } + return point(coordsys(a.el), p/coordsys(a.el)); +} + +/*<asyxml><operator type = "path" signature="cast(explicit arc)"><code></asyxml>*/ +path operator cast(explicit arc a) +{/*<asyxml></code><documentation>Cast arc to path.</documentation></operator></asyxml>*/ + return arctopath(a, arcnodesnumber(a)); +} + +/*<asyxml><operator type = "guide" signature="cast(explicit arc)"><code></asyxml>*/ +guide operator cast(explicit arc a) +{/*<asyxml></code><documentation>Cast arc to guide.</documentation></operator></asyxml>*/ + return arctopath(a, arcnodesnumber(a)); +} + +/*<asyxml><operator type = "arc" signature="*(transform,explicit arc)"><code></asyxml>*/ +arc operator *(transform t, explicit arc a) +{/*<asyxml></code><documentation>Provide transform * arc.</documentation></operator></asyxml>*/ + pair[] P, PP; + path g = arctopath(a, 3); + real a0, a1 = a.angle1, a2 = a.angle2, ap1, ap2; + bool dir = a.direction; + P[0] = t * point(g, 0); + P[1] = t * point(g, 2); + ellipse el = t * a.el; + arc oa; + a0 = (a.angle0 + angle(shiftless(t)))%360; + pair C; + if(a.polarconicroutine == fromCenter) C = el.C; else C = el.F1; + real d = abs(locate(el.F2 - el.F1)) > epsgeo ? + degrees(locate(el.F2 - el.F1)) : a0 + degrees(el.C.coordsys.i); + ap1 = (degrees(P[0]-C, false) - d)%360; + ap2 = (degrees(P[1]-C, false) - d)%360; + oa.init(el, a0, ap1, ap2, a.polarconicroutine, dir); + g = arctopath(oa, 3); + PP[0] = point(g, 0); + PP[1] = point(g, 2); + if((a1 - a2) * (ap1 - ap2) < 0) {// Handle reflection. + dir=!a.direction; + oa.init(el, a0, ap1, ap2, a.polarconicroutine, dir); + } + return oa; +} + +/*<asyxml><operator type = "arc" signature="*(real,explicit arc)"><code></asyxml>*/ +arc operator *(real x, explicit arc a) +{/*<asyxml></code><documentation>Provide real * arc. + Return the arc subtracting and adding '(x - 1) * degrees(a)/2' to 'a.angle1' and 'a.angle2' respectively.</documentation></operator></asyxml>*/ + real a1, a2, gle; + gle = (x - 1) * degrees(a)/2; + a1 = a.angle1 - gle; + a2 = a.angle2 + gle; + arc oa; + oa.init(a.el, a.angle0, a1, a2, a.polarconicroutine, a.direction); + return oa; +} +arc operator *(int x, explicit arc a){return (real)x * a;} +/*<asyxml><operator type = "arc" signature="/(real,explicit arc)"><code></asyxml>*/ +arc operator /(explicit arc a, real x) +{/*<asyxml></code><documentation>Provide arc/real. + Return the arc subtracting and adding '(1/x - 1) * degrees(a)/2' to 'a.angle1' and 'a.angle2' respectively.</documentation></operator></asyxml>*/ + return (1/x) * a; +} +/*<asyxml><operator type = "arc" signature="+(explicit arc,point)"><code></asyxml>*/ +arc operator +(explicit arc a, point M) +{/*<asyxml></code><documentation>Provide arc + point. + Return shifted arc. + 'operator +(explicit arc, point)', 'operator +(explicit arc, vector)' and 'operator -(explicit arc, vector)' are also defined.</documentation></operator></asyxml>*/ + return shift(M) * a; +} +arc operator -(explicit arc a, point M){return a + (-M);} +arc operator +(explicit arc a, vector v){return shift(locate(v)) * a;} +arc operator -(explicit arc a, vector v){return a + (-v);} + + +/*<asyxml><operator type = "bool" signature="@(point,arc)"><code></asyxml>*/ +bool operator @(point M, arc a) +{/*<asyxml></code><documentation>Return true iff 'M' is on the arc 'a'.</documentation></operator></asyxml>*/ + if (!(M @ a.el)) return false; + coordsys R = defaultcoordsys; + path ap = arctopath(a, 3); + line l = line(point(R, point(ap, 0)), point(R, point(ap, 2))); + return sameside(M, point(R, point(ap, 1)), l); +} + +/*<asyxml><function type="void" signature="draw(picture,Label,arc,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/ +void draw(picture pic = currentpicture, Label L = "", arc a, + align align = NoAlign, pen p = currentpen, + arrowbar arrow = None, arrowbar bar = None, margin margin = NoMargin, + Label legend = "", marker marker = nomarker) +{/*<asyxml></code><documentation>Draw 'arc' adding the pen returned by 'addpenarc(p)' to the pen 'p'. + <look href = "#addpenarc"/></documentation></function></asyxml>*/ + draw(pic, L, (path)a, align, addpenarc(p), arrow, bar, margin, legend, marker); +} + +/*<asyxml><function type="real" signature="arclength(arc)"><code></asyxml>*/ +real arclength(arc a) +{/*<asyxml></code><documentation>The arc length of 'a'.</documentation></function></asyxml>*/ + return arclength(a.el, a.angle1, a.angle2, a.direction, a.polarconicroutine); +} + +private point ppoint(arc a, real x) +{// Return the point of the arc proportionally to its length. + point oP; + if(a.el.e == 0) { // Case of circle. + oP = angpoint(a, x * abs(degrees(a))); + } else { // Ellipse and not circle. + if(!a.direction) { + transform t = reflect(line(a.el.F1, a.el.F2)); + return t * ppoint(t * a, x); + } + + real angle1 = a.angle1, angle2 = a.angle2; + if(a.polarconicroutine == fromFocus) { + // dot(point(fromFocus(a.el, angle1, angle1, 1, CCW), 0), 2mm + blue); + // dot(point(fromFocus(a.el, angle2, angle2, 1, CCW), 0), 2mm + blue); + // write("fromfocus1 = ", angle1); + // write("fromfocus2 = ", angle2); + real gle1 = focusToCenter(a.el, angle1); + real gle2 = focusToCenter(a.el, angle2); + if((gle1 - gle2) * (angle1 - angle2) > 0) { + angle1 = gle1; angle2 = gle2; + } else { + angle1 = gle2; angle2 = gle1; + } + // write("fromcenter1 = ", angle1); + // write("fromcenter2 = ", angle2); + // dot(point(fromCenter(a.el, angle1, angle1, 1, CCW), 0), 1mm + red); + // dot(point(fromCenter(a.el, angle2, angle2, 1, CCW), 0), 1mm + red); + } + + if(angle1 > angle2) { + arc ta = a.copy(); + ta.polarconicroutine = fromCenter; + ta.setangles(a0 = a.angle0, a1 = angle1 - 360, a2 = angle2); + return ppoint(ta, x); + } + ellipse co = a.el; + real gle, a1, a2, cx = 0; + bool direction; + if(x >= 0) { + a1 = angle1; + a2 = a1 + 360; + direction = CCW; + } else { + a1 = angle1 - 360; + a2 = a1 - 360; + direction = CW; + } + gle = a1; + real L = arclength(co, angle1, angle2, a.direction, fromCenter); + real tx = L * abs(x)%arclength(co); + real aout = a1; + while(abs(cx - tx) > epsgeo) { + aout = (a1 + a2)/2; + cx = abs(arclength(co, gle, aout, direction, fromCenter)); + if(cx > tx) a2 = (a1 + a2)/2 ; else a1 = (a1 + a2)/2; + } + pair p = point(arcfromcenter(co, aout, aout, CCW, 1), 0); + oP = point(coordsys(co), p/coordsys(co)); + } + return oP; +} + +/*<asyxml><function type="point" signature="point(arc,abscissa)"><code></asyxml>*/ +point point(arc a, abscissa l) +{/*<asyxml></code><documentation>Return the point of 'a' which has the abscissa 'l.x' + according to the abscissa system 'l.system'. + Note that 'a.polarconicroutine' is used instead of 'l.polarconicroutine'. + <look href = "#struct abscissa"/></documentation></function></asyxml>*/ + real posx; + arc ta = a.copy(); + ellipse co = a.el; + if (l.system == relativesystem) { + posx = l.x; + } else + if (l.system == curvilinearsystem) { + real tl; + if(co.e == 0) { + tl = curabscissa(a.el, angpoint(a.el, a.angle0 + a.angle1)).x; + return curpoint(a.el, tl + (a.direction ? l.x : -l.x)); + } else { + tl = curabscissa(a.el, angpoint(a.el, a.angle1, a.polarconicroutine)).x; + return curpoint(a.el, tl + (a.direction ? l.x : -l.x)); + } + } else + if (l.system == nodesystem) { + coordsys R = coordsys(co); + return point(R, point((path)a, l.x)/R); + } else + if (l.system == angularsystem) { + return angpoint(a, l.x); + } else abort("point: bad abscissa system."); + return ppoint(ta, posx); +} + + +/*<asyxml><function type="point" signature="point(arc,real)"><code></asyxml>*/ +point point(arc a, real x) +{/*<asyxml></code><documentation>Return the point between node floor(t) and floor(t) + 1.</documentation></function></asyxml>*/ + return point(a, nodabscissa(x)); +} +pair point(explicit arc a, int x) +{ + return point(a, nodabscissa(x)); +} + +/*<asyxml><function type="point" signature="relpoint(arc,real)"><code></asyxml>*/ +point relpoint(arc a, real x) +{/*<asyxml></code><documentation>Return the relative point of 'a'. + If x > 1 or x < 0, the returned point is on the extended arc.</documentation></function></asyxml>*/ + return point(a, relabscissa(x)); +} + +/*<asyxml><function type="point" signature="curpoint(arc,real)"><code></asyxml>*/ +point curpoint(arc a, real x) +{/*<asyxml></code><documentation>Return the point of 'a' which has the curvilinear abscissa 'x'. + If x < 0 or x > arclength(a), the returned point is on the extended arc.</documentation></function></asyxml>*/ + return point(a, curabscissa(x)); +} + +/*<asyxml><function type="abscissa" signature="angabscissa(arc,point)"><code></asyxml>*/ +abscissa angabscissa(arc a, point M) +{/*<asyxml></code><documentation>Return the angular abscissa of 'M' according to the arc 'a'.</documentation></function></asyxml>*/ + if(!(M @ a.el)) + abort("angabscissa: the point is not on the extended arc."); + abscissa oa; + oa.system = angularsystem; + oa.polarconicroutine = a.polarconicroutine; + real am = angabscissa(a.el, M, a.polarconicroutine).x; + oa.x = (am - a.angle1 - (a.el.e == 0 ? a.angle0 : 0))%360; + oa.x = a.direction ? oa.x : 360 - oa.x; + return oa; +} + +/*<asyxml><function type="abscissa" signature="curabscissa(arc,point)"><code></asyxml>*/ +abscissa curabscissa(arc a, point M) +{/*<asyxml></code><documentation>Return the curvilinear abscissa according to the arc 'a'.</documentation></function></asyxml>*/ + ellipse el = a.el; + if(!(M @ el)) + abort("angabscissa: the point is not on the extended arc."); + abscissa oa; + oa.system = curvilinearsystem; + real xm = curabscissa(el, M).x; + real a0 = el.e == 0 ? a.angle0 : 0; + real am = curabscissa(el, angpoint(el, a.angle1 + a0, a.polarconicroutine)).x; + real l = arclength(el); + oa.x = (xm - am)%l; + oa.x = a.direction ? oa.x : l - oa.x; + return oa; +} + +/*<asyxml><function type="abscissa" signature="nodabscissa(arc,point)"><code></asyxml>*/ +abscissa nodabscissa(arc a, point M) +{/*<asyxml></code><documentation>Return the node abscissa according to the arc 'a'.</documentation></function></asyxml>*/ + if(!(M @ a)) + abort("nodabscissa: the point is not on the arc."); + abscissa oa; + oa.system = nodesystem; + oa.x = intersect((path)a, M)[0]; + return oa; +} + +/*<asyxml><function type="abscissa" signature="relabscissa(arc,point)"><code></asyxml>*/ +abscissa relabscissa(arc a, point M) +{/*<asyxml></code><documentation>Return the relative abscissa according to the arc 'a'.</documentation></function></asyxml>*/ + ellipse el = a.el; + if(!( M @ el)) + abort("relabscissa: the point is not on the prolonged arc."); + abscissa oa; + oa.system = relativesystem; + oa.x = curabscissa(a, M).x/arclength(a); + return oa; +} + +/*<asyxml><function type="void" signature="markarc(picture,Label,int,real,real,arc,arrowbar,pen,pen,margin,marker)"><code></asyxml>*/ +void markarc(picture pic = currentpicture, + Label L = "", + int n = 1, real radius = 0, real space = 0, + arc a, + pen sectorpen = currentpen, + pen markpen = sectorpen, + margin margin = NoMargin, + arrowbar arrow = None, + marker marker = nomarker) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + real Da = degrees(a); + pair p1 = point(a, 0); + pair p2 = relpoint(a, 1); + pair c = a.polarconicroutine == fromCenter ? locate(a.el.C) : locate(a.el.F1); + if(radius == 0) radius = markangleradius(markpen); + if(abs(Da) > 180) radius = -radius; + radius = (a.direction ? 1 : -1) * sgnd(Da) * radius; + draw(c--p1^^c--p2, sectorpen); + markangle(pic = pic, L = L, n = n, radius = radius, space = space, + A = p1, O = c, B = p2, + arrow = arrow, p = markpen, margin = margin, + marker = marker); +} +// *.........................ARCS..........................* +// *=======================================================* + +// *=======================================================* +// *........................MASSES.........................* +/*<asyxml><struct signature="mass"><code></asyxml>*/ +struct mass {/*<asyxml></code><documentation></documentation><property type = "point" signature="M"><code></asyxml>*/ + point M;/*<asyxml></code><documentation></documentation></property><property type = "real" signature="m"><code></asyxml>*/ + real m;/*<asyxml></code><documentation></documentation></property></asyxml>*/ +}/*<asyxml></struct></asyxml>*/ + +/*<asyxml><function type="mass" signature="mass(point,real)"><code></asyxml>*/ +mass mass(point M, real m) +{/*<asyxml></code><documentation>Constructor of mass point.</documentation></function></asyxml>*/ + mass om; + om.M = M; + om.m = m; + return om; +} + +/*<asyxml><operator type = "point" signature="cast(mass)"><code></asyxml>*/ +point operator cast(mass m) +{/*<asyxml></code><documentation>Cast mass point to point.</documentation></operator></asyxml>*/ + point op; + op = m.M; + op.m = m.m; + return op; +} +/*<asyxml><function type="point" signature="point(explicit mass)"><code></asyxml>*/ +point point(explicit mass m){return m;}/*<asyxml></code><documentation>Cast + 'm' to point</documentation></function></asyxml>*/ + +/*<asyxml><operator type = "mass" signature="cast(point)"><code></asyxml>*/ +mass operator cast(point M) +{/*<asyxml></code><documentation>Cast point to mass point.</documentation></operator></asyxml>*/ + mass om; + om.M = M; + om.m = M.m; + return om; +} +/*<asyxml><function type="mass" signature="mass(explicit point)"><code></asyxml>*/ +mass mass(explicit point P) +{/*<asyxml></code><documentation>Cast 'P' to mass.</documentation></function></asyxml>*/ + return mass(P, P.m); +} + +/*<asyxml><operator type = "point[]" signature="cast(mass[])"><code></asyxml>*/ +point[] operator cast(mass[] m) +{/*<asyxml></code><documentation>Cast mass[] to point[].</documentation></operator></asyxml>*/ + point[] op; + for(mass am : m) op.push(point(am)); + return op; +} + +/*<asyxml><operator type = "mass[]" signature="cast(point[])"><code></asyxml>*/ +mass[] operator cast(point[] P) +{/*<asyxml></code><documentation>Cast point[] to mass[].</documentation></operator></asyxml>*/ + mass[] om; + for(point op : P) om.push(mass(op)); + return om; +} + +/*<asyxml><function type="mass" signature="mass(coordsys,explicit pair,real)"><code></asyxml>*/ +mass mass(coordsys R, explicit pair p, real m) +{/*<asyxml></code><documentation>Return the mass which has coordinates + 'p' with respect to 'R' and weight 'm'.</documentation></function></asyxml>*/ + return point(R, p, m);// Using casting. +} + +/*<asyxml><operator type = "mass" signature="cast(pair)"><code></asyxml>*/ +mass operator cast(pair m){return mass((point)m, 1);}/*<asyxml></code><documentation>Cast pair to mass point.</documentation></operator></asyxml>*/ +/*<asyxml><operator type = "path" signature="cast(mass)"><code></asyxml>*/ +path operator cast(mass M){return M.M;}/*<asyxml></code><documentation>Cast mass point to path.</documentation></operator></asyxml>*/ +/*<asyxml><operator type = "guide" signature="cast(mass)"><code></asyxml>*/ +guide operator cast(mass M){return M.M;}/*<asyxml></code><documentation>Cast mass to guide.</documentation></operator></asyxml>*/ + +/*<asyxml><operator type = "mass" signature="+(mass,mass)"><code></asyxml>*/ +mass operator +(mass M1, mass M2) +{/*<asyxml></code><documentation>Provide mass + mass. + mass - mass is also defined.</documentation></operator></asyxml>*/ + return mass(M1.M + M2.M, M1.m + M2.m); +} +mass operator -(mass M1, mass M2) +{ + return mass(M1.M - M2.M, M1.m - M2.m); +} + +/*<asyxml><operator type = "mass" signature="*(real,mass)"><code></asyxml>*/ +mass operator *(real x, explicit mass M) +{/*<asyxml></code><documentation>Provide real * mass. + The resulted mass is the mass of 'M' multiplied by 'x' . + mass/real, mass + real and mass - real are also defined.</documentation></operator></asyxml>*/ + return mass(M.M, x * M.m); +} +mass operator *(int x, explicit mass M){return mass(M.M, x * M.m);} +mass operator /(explicit mass M, real x){return mass(M.M, M.m/x);} +mass operator /(explicit mass M, int x){return mass(M.M, M.m/x);} +mass operator +(explicit mass M, real x){return mass(M.M, M.m + x);} +mass operator +(explicit mass M, int x){return mass(M.M, M.m + x);} +mass operator -(explicit mass M, real x){return mass(M.M, M.m - x);} +mass operator -(explicit mass M, int x){return mass(M.M, M.m - x);} +/*<asyxml><operator type = "mass" signature="*(transform,mass)"><code></asyxml>*/ +mass operator *(transform t, mass M) +{/*<asyxml></code><documentation>Provide transform * mass.</documentation></operator></asyxml>*/ + return mass(t * M.M, M.m); +} + +/*<asyxml><function type="mass" signature="masscenter(... mass[])"><code></asyxml>*/ +mass masscenter(... mass[] M) +{/*<asyxml></code><documentation>Return the center of the masses 'M'.</documentation></function></asyxml>*/ + point[] P; + for (int i = 0; i < M.length; ++i) + P.push(M[i].M); + P = standardizecoordsys(currentcoordsys, true ... P); + real m = M[0].m; + point oM = M[0].m * P[0]; + for (int i = 1; i < M.length; ++i) { + oM += M[i].m * P[i]; + m += M[i].m; + } + if (m == 0) abort("masscenter: the sum of masses is null."); + return mass(oM/m, m); +} + +/*<asyxml><function type="string" signature="massformat(string,string,mass)"><code></asyxml>*/ +string massformat(string format = defaultmassformat, + string s, mass M) +{/*<asyxml></code><documentation>Return the string formated by 'format' with the mass value. + In the parameter 'format', %L will be replaced by 's'. + <look href = "#defaultmassformat"/>.</documentation></function></asyxml>*/ + return format == "" ? s : + format(replace(format, "%L", replace(s, "$", "")), M.m); +} + +/*<asyxml><function type="void" signature="label(picture,Label,explicit mass,align,string,pen,filltype)"><code></asyxml>*/ +void label(picture pic = currentpicture, Label L, explicit mass M, + align align = NoAlign, string format = defaultmassformat, + pen p = nullpen, filltype filltype = NoFill) +{/*<asyxml></code><documentation>Draw label returned by massformat(format, L, M) at coordinates of M. + <look href = "#massformat(string, string, mass)"/>.</documentation></function></asyxml>*/ + Label lL = L.copy(); + lL.s = massformat(format, lL.s, M); + Label L = Label(lL, M.M, align, p, filltype); + add(pic, L); +} + +/*<asyxml><function type="void" signature="dot(picture,Label,explicit mass,align,string,pen)"><code></asyxml>*/ +void dot(picture pic = currentpicture, Label L, explicit mass M, align align = NoAlign, + string format = defaultmassformat, pen p = currentpen) +{/*<asyxml></code><documentation>Draw a dot with label 'L' as + label(picture, Label, explicit mass, align, string, pen, filltype) does. + <look href = "#label(picture, Label, mass, align, string, pen, filltype)"/>.</documentation></function></asyxml>*/ + Label lL = L.copy(); + lL.s = massformat(format, lL.s, M); + lL.position(locate(M.M)); + lL.align(align, E); + lL.p(p); + dot(pic, M.M, p); + add(pic, lL); +} +// *........................MASSES.........................* +// *=======================================================* + +// *=======================================================* +// *.......................TRIANGLES.......................* +/*<asyxml><function type="point" signature="orthocentercenter(point,point,point)"><code></asyxml>*/ +point orthocentercenter(point A, point B, point C) +{/*<asyxml></code><documentation>Return the orthocenter of the triangle ABC.</documentation></function></asyxml>*/ + point[] P = standardizecoordsys(A, B, C); + coordsys R = P[0].coordsys; + pair pp = extension(A, projection(P[1], P[2]) * P[0], B, projection(P[0], P[2]) * P[1]); + return point(R, pp/R); +} + +/*<asyxml><function type="point" signature="centroid(point,point,point)"><code></asyxml>*/ +point centroid(point A, point B, point C) +{/*<asyxml></code><documentation>Return the centroid of the triangle ABC.</documentation></function></asyxml>*/ + return (A + B + C)/3; +} + +/*<asyxml><function type="point" signature="incenter(point,point,point)"><code></asyxml>*/ +point incenter(point A, point B, point C) +{/*<asyxml></code><documentation>Return the center of the incircle of the triangle ABC.</documentation></function></asyxml>*/ + point[] P = standardizecoordsys(A, B, C); + coordsys R = P[0].coordsys; + pair a = A, b = B, c = C; + pair pp = extension(a, a + dir(a--b, a--c), b, b + dir(b--a, b--c)); + return point(R, pp/R); +} + +/*<asyxml><function type="real" signature="inradius(point,point,point)"><code></asyxml>*/ +real inradius(point A, point B, point C) +{/*<asyxml></code><documentation>Return the radius of the incircle of the triangle ABC.</documentation></function></asyxml>*/ + point IC = incenter(A, B, C); + return abs(IC - projection(A, B) * IC); +} + +/*<asyxml><function type="circle" signature="incircle(point,point,point)"><code></asyxml>*/ +circle incircle(point A, point B, point C) +{/*<asyxml></code><documentation>Return the incircle of the triangle ABC.</documentation></function></asyxml>*/ + point IC = incenter(A, B, C); + return circle(IC, abs(IC - projection(A, B) * IC)); +} + +/*<asyxml><function type="point" signature="excenter(point,point,point)"><code></asyxml>*/ +point excenter(point A, point B, point C) +{/*<asyxml></code><documentation>Return the center of the excircle of the triangle tangent with (AB).</documentation></function></asyxml>*/ + point[] P = standardizecoordsys(A, B, C); + coordsys R = P[0].coordsys; + pair a = A, b = B, c = C; + pair pp = extension(a, a + rotate(90) * dir(a--b, a--c), b, b + rotate(90) * dir(b--a, b--c)); + return point(R, pp/R); +} + +/*<asyxml><function type="real" signature="exradius(point,point,point)"><code></asyxml>*/ +real exradius(point A, point B, point C) +{/*<asyxml></code><documentation>Return the radius of the excircle of the triangle ABC with (AB).</documentation></function></asyxml>*/ + point EC = excenter(A, B, C); + return abs(EC - projection(A, B) * EC); +} + +/*<asyxml><function type="circle" signature="excircle(point,point,point)"><code></asyxml>*/ +circle excircle(point A, point B, point C) +{/*<asyxml></code><documentation>Return the excircle of the triangle ABC tangent with (AB).</documentation></function></asyxml>*/ + point center = excenter(A, B, C); + real radius = abs(center - projection(B, C) * center); + return circle(center, radius); +} + +private int[] numarray = {1, 2, 3}; +numarray.cyclic = true; + +/*<asyxml><struct signature="triangle"><code></asyxml>*/ +struct triangle {/*<asyxml></code><documentation></documentation></asyxml>*/ + + /*<asyxml><struct signature="vertex"><code></asyxml>*/ + struct vertex {/*<asyxml></code><documentation>Structure used to communicate the vertex of a triangle.</documentation><property type = "int" signature="n"><code></asyxml>*/ + int n;/*<asyxml></code><documentation>1 means VA,2 means VB,3 means VC,4 means VA etc...</documentation></property><property type = "triangle" signature="triangle"><code></asyxml>*/ + triangle t;/*<asyxml></code><documentation>The triangle to which the vertex refers.</documentation></property></asyxml>*/ + }/*<asyxml></struct></asyxml>*/ + + /*<asyxml><property type = "point" signature="A,B,C"><code></asyxml>*/ + restricted point A, B, C;/*<asyxml></code><documentation>The vertices of the triangle (as point).</documentation></property><property type = "vertex" signature="VA, VB, VC"><code></asyxml>*/ + restricted vertex VA, VB, VC;/*<asyxml></code><documentation>The vertices of the triangle (as vertex). + Note that the vertex structure contains the triangle to wish it refers.</documentation></property></asyxml>*/ + VA.n = 1;VB.n = 2;VC.n = 3; + + /*<asyxml><method type = "vertex" signature="vertex(int)"><code></asyxml>*/ + vertex vertex(int n) + {/*<asyxml></code><documentation>Return numbered vertex. + 'n' is 1 means VA, 2 means VB, 3 means VC, 4 means VA etc...</documentation></method></asyxml>*/ + n = numarray[n - 1]; + if(n == 1) return VA; + else if(n == 2) return VB; + return VC; + } + + /*<asyxml><method type = "point" signature="point(int)"><code></asyxml>*/ + point point(int n) + {/*<asyxml></code><documentation>Return numbered point. + n is 1 means A, 2 means B, 3 means C, 4 means A etc...</documentation></method></asyxml>*/ + n = numarray[n - 1]; + if(n == 1) return A; + else if(n == 2) return B; + return C; + } + + /*<asyxml><method type = "void" signature="init(point,point,point)"><code></asyxml>*/ + void init(point A, point B, point C) + {/*<asyxml></code><documentation>Constructor.</documentation></method></asyxml>*/ + point[] P = standardizecoordsys(A, B, C); + this.A = P[0]; + this.B = P[1]; + this.C = P[2]; + VA.t = this; VB.t = this; VC.t = this; + } + + /*<asyxml><method type = "void" signature="operator init(point,point,point)"><code></asyxml>*/ + void operator init(point A, point B, point C) + {/*<asyxml></code><documentation>For backward compatibility. + Provide the routine 'triangle(point A, point B, point C)'.</documentation></method></asyxml>*/ + this.init(A, B, C); + } + + /*<asyxml><method type = "void" signature="init(real,real,real,real,point)"><code></asyxml>*/ + void operator init(real b, real alpha, real c, real angle = 0, point A = (0, 0)) + {/*<asyxml></code><documentation>For backward compatibility. + Provide the routine 'triangle(real b, real alpha, real c, real angle = 0, point A = (0, 0)) + which returns the triangle ABC rotated by 'angle' (in degrees) and where b = AC, degrees(A) = alpha, AB = c.</documentation></method></asyxml>*/ + coordsys R = A.coordsys; + this.init(A, A + R.polar(c, radians(angle)), A + R.polar(b, radians(angle + alpha))); + } + + /*<asyxml><method type = "real" signature="a(),b(),c()"><code></asyxml>*/ + real a() + {/*<asyxml></code><documentation>Return the length BC. + b() and c() are also defined and return the length AC and AB respectively.</documentation></method></asyxml>*/ + return length(C - B); + } + real b() {return length(A - C);} + real c() {return length(B - A);} + + private real det(pair a, pair b) {return a.x * b.y - a.y * b.x;} + + /*<asyxml><method type = "real" signature="area()"><code></asyxml>*/ + real area() + {/*<asyxml></code><documentation></documentation></method></asyxml>*/ + pair a = locate(A), b = locate(B), c = locate(C); + return 0.5 * abs(det(a, b) + det(b, c) + det(c, a)); + } + + /*<asyxml><method type = "real" signature="alpha(),beta(),gamma()"><code></asyxml>*/ + real alpha() + {/*<asyxml></code><documentation>Return the measure (in degrees) of the angle A. + beta() and gamma() are also defined and return the measure of the angles B and C respectively.</documentation></method></asyxml>*/ + return degrees(acos((b()^2 + c()^2 - a()^2)/(2b() * c()))); + } + real beta() {return degrees(acos((c()^2 + a()^2 - b()^2)/(2c() * a())));} + real gamma() {return degrees(acos((a()^2 + b()^2 - c()^2)/(2a() * b())));} + + /*<asyxml><method type = "path" signature="Path()"><code></asyxml>*/ + path Path() + {/*<asyxml></code><documentation>The path of the triangle.</documentation></method></asyxml>*/ + return A--C--B--cycle; + } + + /*<asyxml><struct signature="side"><code></asyxml>*/ + struct side + {/*<asyxml></code><documentation>Structure used to communicate the side of a triangle.</documentation><property type = "int" signature="n"><code></asyxml>*/ + int n;/*<asyxml></code><documentation>1 or 0 means [AB],-1 means [BA],2 means [BC],-2 means [CB] etc.</documentation></property><property type = "triangle" signature="triangle"><code></asyxml>*/ + triangle t;/*<asyxml></code><documentation>The triangle to which the side refers.</documentation></property></asyxml>*/ + }/*<asyxml></struct></asyxml>*/ + + /*<asyxml><property type = "side" signature="AB"><code></asyxml>*/ + side AB;/*<asyxml></code><documentation>For the routines using the structure 'side', triangle.AB means 'side AB'. + BA, AC, CA etc are also defined.</documentation></property></asyxml>*/ + AB.n = 1; AB.t = this; + side BA; BA.n = -1; BA.t = this; + side BC; BC.n = 2; BC.t = this; + side CB; CB.n = -2; CB.t = this; + side CA; CA.n = 3; CA.t = this; + side AC; AC.n = -3; AC.t = this; + + /*<asyxml><method type = "side" signature="side(int)"><code></asyxml>*/ + side side(int n) + {/*<asyxml></code><documentation>Return numbered side. + n is 1 means AB, -1 means BA, 2 means BC, -2 means CB, etc.</documentation></method></asyxml>*/ + if(n == 0) abort('Invalid side number.'); + int an = numarray[abs(n)-1]; + if(an == 1) return n > 0 ? AB : BA; + else if(an == 2) return n > 0 ? BC : CB; + return n > 0 ? CA : AC; + } + + /*<asyxml><method type = "line" signature="line(int)"><code></asyxml>*/ + line line(int n) + {/*<asyxml></code><documentation>Return the numbered line.</documentation></method></asyxml>*/ + if(n == 0) abort('Invalid line number.'); + int an = numarray[abs(n)-1]; + if(an == 1) return n > 0 ? line(A, B) : line(B, A); + else if(an == 2) return n > 0 ? line(B, C) : line(C, B); + return n > 0 ? line(C, A) : line(A, C); + } + +}/*<asyxml></struct></asyxml>*/ + +from triangle unravel side; // The structure 'side' is now available outside the triangle structure. +from triangle unravel vertex; // The structure 'vertex' is now available outside the triangle structure. + +triangle[] operator ^^(triangle[] t1, triangle t2) +{ + triangle[] T; + for (int i = 0; i < t1.length; ++i) T.push(t1[i]); + T.push(t2); + return T; +} + +triangle[] operator ^^(... triangle[] t) +{ + triangle[] T; + for (int i = 0; i < t.length; ++i) { + T.push(t[i]); + } + return T; +} + +/*<asyxml><operator type = "line" signature="cast(side)"><code></asyxml>*/ +line operator cast(side side) +{/*<asyxml></code><documentation>Cast side to (infinite) line. + Most routine with line parameters works with side parameters. + One can use the code 'segment(a_side)' to obtain a line segment.</documentation></operator></asyxml>*/ + triangle t = side.t; + return t.line(side.n); +} + +/*<asyxml><function type="line" signature="line(explicit side)"><code></asyxml>*/ +line line(explicit side side) +{/*<asyxml></code><documentation>Return 'side' as line.</documentation></function></asyxml>*/ + return (line)side; +} + +/*<asyxml><function type="segment" signature="segment(explicit side)"><code></asyxml>*/ +segment segment(explicit side side) +{/*<asyxml></code><documentation>Return 'side' as segment.</documentation></function></asyxml>*/ + return (segment)(line)side; +} + +/*<asyxml><operator type = "point" signature="cast(vertex)"><code></asyxml>*/ +point operator cast(vertex V) +{/*<asyxml></code><documentation>Cast vertex to point. + Most routine with point parameters works with vertex parameters.</documentation></operator></asyxml>*/ + return V.t.point(V.n); +} + +/*<asyxml><function type="point" signature="point(explicit vertex)"><code></asyxml>*/ +point point(explicit vertex V) +{/*<asyxml></code><documentation>Return the point corresponding to the vertex 'V'.</documentation></function></asyxml>*/ + return (point)V; +} + +/*<asyxml><function type="side" signature="opposite(vertex)"><code></asyxml>*/ +side opposite(vertex V) +{/*<asyxml></code><documentation>Return the opposite side of vertex 'V'.</documentation></function></asyxml>*/ + return V.t.side(numarray[abs(V.n)]); +} + +/*<asyxml><function type="vertex" signature="opposite(side)"><code></asyxml>*/ +vertex opposite(side side) +{/*<asyxml></code><documentation>Return the opposite vertex of side 'side'.</documentation></function></asyxml>*/ + return side.t.vertex(numarray[abs(side.n) + 1]); +} + +/*<asyxml><function type="point" signature="midpoint(side)"><code></asyxml>*/ +point midpoint(side side) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return midpoint(segment(side)); +} + +/*<asyxml><operator type = "triangle" signature="*(transform,triangle)"><code></asyxml>*/ +triangle operator *(transform T, triangle t) +{/*<asyxml></code><documentation>Provide transform * triangle.</documentation></operator></asyxml>*/ + return triangle(T * t.A, T * t.B, T * t.C); +} + +/*<asyxml><function type="triangle" signature="triangleAbc(real,real,real,real,point)"><code></asyxml>*/ +triangle triangleAbc(real alpha, real b, real c, real angle = 0, point A = (0, 0)) +{/*<asyxml></code><documentation>Return the triangle ABC rotated by 'angle' with BAC = alpha, AC = b and AB = c.</documentation></function></asyxml>*/ + triangle T; + coordsys R = A.coordsys; + T.init(A, A + R.polar(c, radians(angle)), A + R.polar(b, radians(angle + alpha))); + return T; +} + +/*<asyxml><function type="triangle" signature="triangleabc(real,real,real,real,point)"><code></asyxml>*/ +triangle triangleabc(real a, real b, real c, real angle = 0, point A = (0, 0)) +{/*<asyxml></code><documentation>Return the triangle ABC rotated by 'angle' with BC = a, AC = b and AB = c.</documentation></function></asyxml>*/ + triangle T; + coordsys R = A.coordsys; + T.init(A, A + R.polar(c, radians(angle)), A + R.polar(b, radians(angle) + acos((b^2 + c^2 - a^2)/(2 * b * c)))); + return T; +} + +/*<asyxml><function type="triangle" signature="triangle(line,line,line)"><code></asyxml>*/ +triangle triangle(line l1, line l2, line l3) +{/*<asyxml></code><documentation>Return the triangle defined by three line.</documentation></function></asyxml>*/ + point P1, P2, P3; + P1 = intersectionpoint(l1, l2); + P2 = intersectionpoint(l1, l3); + P3 = intersectionpoint(l2, l3); + if(!(defined(P1) && defined(P2) && defined(P3))) abort("triangle: two lines are parallel."); + return triangle(P1, P2, P3); +} + +/*<asyxml><function type="point" signature="foot(vertex)"><code></asyxml>*/ +point foot(vertex V) +{/*<asyxml></code><documentation>Return the endpoint of the altitude from V.</documentation></function></asyxml>*/ + return projection((line)opposite(V)) * ((point)V); +} + +/*<asyxml><function type="point" signature="foot(side)"><code></asyxml>*/ +point foot(side side) +{/*<asyxml></code><documentation>Return the endpoint of the altitude on 'side'.</documentation></function></asyxml>*/ + return projection((line)side) * point(opposite(side)); +} + +/*<asyxml><function type="line" signature="altitude(vertex)"><code></asyxml>*/ +line altitude(vertex V) +{/*<asyxml></code><documentation>Return the altitude passing through 'V'.</documentation></function></asyxml>*/ + return line(point(V), foot(V)); +} + +/*<asyxml><function type="line" signature="altitude(vertex)"><code></asyxml>*/ +line altitude(side side) +{/*<asyxml></code><documentation>Return the altitude cutting 'side'.</documentation></function></asyxml>*/ + return altitude(opposite(side)); +} + +/*<asyxml><function type="point" signature="orthocentercenter(triangle)"><code></asyxml>*/ +point orthocentercenter(triangle t) +{/*<asyxml></code><documentation>Return the orthocenter of the triangle t.</documentation></function></asyxml>*/ + return orthocentercenter(t.A, t.B, t.C); +} + +/*<asyxml><function type="point" signature="centroid(triangle)"><code></asyxml>*/ +point centroid(triangle t) +{/*<asyxml></code><documentation>Return the centroid of the triangle 't'.</documentation></function></asyxml>*/ + return (t.A + t.B + t.C)/3; +} + +/*<asyxml><function type="point" signature="circumcenter(triangle)"><code></asyxml>*/ +point circumcenter(triangle t) +{/*<asyxml></code><documentation>Return the circumcenter of the triangle 't'.</documentation></function></asyxml>*/ + return circumcenter(t.A, t.B, t.C); +} + +/*<asyxml><function type="circle" signature="circle(triangle)"><code></asyxml>*/ +circle circle(triangle t) +{/*<asyxml></code><documentation>Return the circumcircle of the triangle 't'.</documentation></function></asyxml>*/ + return circle(t.A, t.B, t.C); +} + +/*<asyxml><function type="circle" signature="circumcircle(triangle)"><code></asyxml>*/ +circle circumcircle(triangle t) +{/*<asyxml></code><documentation>Return the circumcircle of the triangle 't'.</documentation></function></asyxml>*/ + return circle(t.A, t.B, t.C); +} + +/*<asyxml><function type="point" signature="incenter(triangle)"><code></asyxml>*/ +point incenter(triangle t) +{/*<asyxml></code><documentation>Return the center of the incircle of the triangle 't'.</documentation></function></asyxml>*/ + return incenter(t.A, t.B, t.C); +} + +/*<asyxml><function type="real" signature="inradius(triangle)"><code></asyxml>*/ +real inradius(triangle t) +{/*<asyxml></code><documentation>Return the radius of the incircle of the triangle 't'.</documentation></function></asyxml>*/ + return inradius(t.A, t.B, t.C); +} + +/*<asyxml><function type="circle" signature="incircle(triangle)"><code></asyxml>*/ +circle incircle(triangle t) +{/*<asyxml></code><documentation>Return the the incircle of the triangle 't'.</documentation></function></asyxml>*/ + return incircle(t.A, t.B, t.C); +} + +/*<asyxml><function type="point" signature="excenter(side,triangle)"><code></asyxml>*/ +point excenter(side side) +{/*<asyxml></code><documentation>Return the center of the excircle tangent with the side 'side' of its triangle. + side = 0 means AB, 1 means AC, other means BC. + One must use the predefined sides t.AB, t.AC where 't' is a triangle....</documentation></function></asyxml>*/ + point op; + triangle t = side.t; + int n = numarray[abs(side.n) - 1]; + if(n == 1) op = excenter(t.A, t.B, t.C); + else if(n == 2) op = excenter(t.B, t.C, t.A); + else op = excenter(t.C, t.A, t.B); + return op; +} + +/*<asyxml><function type="real" signature="exradius(side,triangle)"><code></asyxml>*/ +real exradius(side side) +{/*<asyxml></code><documentation>Return radius of the excircle tangent with the side 'side' of its triangle. + side = 0 means AB, 1 means BC, other means CA. + One must use the predefined sides t.AB, t.AC where 't' is a triangle....</documentation></function></asyxml>*/ + real or; + triangle t = side.t; + int n = numarray[abs(side.n) - 1]; + if(n == 1) or = exradius(t.A, t.B, t.C); + else if(n == 2) or = exradius(t.B, t.C, t.A); + else or = exradius(t.A, t.C, t.B); + return or; +} + +/*<asyxml><function type="circle" signature="excircle(side,triangle)"><code></asyxml>*/ +circle excircle(side side) +{/*<asyxml></code><documentation>Return the excircle tangent with the side 'side' of its triangle. + side = 0 means AB, 1 means AC, other means BC. + One must use the predefined sides t.AB, t.AC where 't' is a triangle....</documentation></function></asyxml>*/ + circle oc; + int n = numarray[abs(side.n) - 1]; + triangle t = side.t; + if(n == 1) oc = excircle(t.A, t.B, t.C); + else if(n == 2) oc = excircle(t.B, t.C, t.A); + else oc = excircle(t.A, t.C, t.B); + return oc; +} + +/*<asyxml><struct signature="trilinear"><code></asyxml>*/ +struct trilinear +{/*<asyxml></code><documentation>Trilinear coordinates 'a:b:c' relative to triangle 't'. + <url href = "http://mathworld.wolfram.com/TrilinearCoordinates.html"/></documentation><property type = "real" signature="a,b,c"><code></asyxml>*/ + real a,b,c;/*<asyxml></code><documentation>The trilinear coordinates.</documentation></property><property type = "triangle" signature="t"><code></asyxml>*/ + triangle t;/*<asyxml></code><documentation>The reference triangle.</documentation></property></asyxml>*/ +}/*<asyxml></struct></asyxml>*/ + +/*<asyxml><function type="trilinear" signature="trilinear(triangle,real,real,real)"><code></asyxml>*/ +trilinear trilinear(triangle t, real a, real b, real c) +{/*<asyxml></code><documentation>Return the trilinear coordinates relative to 't'. + <url href = "http://mathworld.wolfram.com/TrilinearCoordinates.html"/></documentation></function></asyxml>*/ + trilinear ot; + ot.a = a; ot.b = b; ot.c = c; + ot.t = t; + return ot; +} + +/*<asyxml><function type="trilinear" signature="trilinear(triangle,point)"><code></asyxml>*/ +trilinear trilinear(triangle t, point M) +{/*<asyxml></code><documentation>Return the trilinear coordinates of 'M' relative to 't'. + <url href = "http://mathworld.wolfram.com/TrilinearCoordinates.html"/></documentation></function></asyxml>*/ + trilinear ot; + pair m = locate(M); + int sameside(pair A, pair B, pair m, pair p) + {// Return 1 if 'm' and 'p' are same side of line (AB) else return -1. + pair mil = (A + B)/2; + pair mA = rotate(90, mil) * A; + pair mB = rotate(-90, mil) * A; + return (abs(m - mA) <= abs(m - mB)) == (abs(p - mA) <= abs(p - mB)) ? 1 : -1; + } + real det(pair a, pair b) {return a.x * b.y - a.y * b.x;} + real area(pair a, pair b, pair c){return 0.5 * abs(det(a, b) + det(b, c) + det(c, a));} + pair A = t.A, B = t.B, C = t.C; + real t1 = area(B, C, m), t2 = area(C, A, m), t3 = area(A, B, m); + ot.a = sameside(B, C, A, m) * t1/t.a(); + ot.b = sameside(A, C, B, m) * t2/t.b(); + ot.c = sameside(A, B, C, m) * t3/t.c(); + ot.t = t; + return ot; +} + +/*<asyxml><function type="void" signature="write(trilinear)"><code></asyxml>*/ +void write(trilinear tri) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + write(format("%f : ", tri.a) + format("%f : ", tri.b) + format("%f", tri.c)); +} + +/*<asyxml><function type="point" signature="trilinear(triangle,real,real,real)"><code></asyxml>*/ +point point(trilinear tri) +{/*<asyxml></code><documentation>Return the trilinear coordinates relative to 't'. + <url href = "http://mathworld.wolfram.com/TrilinearCoordinates.html"/></documentation></function></asyxml>*/ + triangle t = tri.t; + return masscenter(0.5 * t.a() * mass(t.A, tri.a), + 0.5 * t.b() * mass(t.B, tri.b), + 0.5 * t.c() * mass(t.C, tri.c)); +} + +/*<asyxml><function type="int[]" signature="tricoef(side)"><code></asyxml>*/ +int[] tricoef(side side) +{/*<asyxml></code><documentation>Return an array of integer (values are 0 or 1) which represents 'side'. + For example, side = t.BC will be represented by {0, 1, 1}.</documentation></function></asyxml>*/ + int[] oi; + int n = numarray[abs(side.n) - 1]; + oi.push((n == 1 || n == 3) ? 1 : 0); + oi.push((n == 1 || n == 2) ? 1 : 0); + oi.push((n == 2 || n == 3) ? 1 : 0); + return oi; +} + +/*<asyxml><operator type = "point" signature="cast(trilinear)"><code></asyxml>*/ +point operator cast(trilinear tri) +{/*<asyxml></code><documentation>Cast trilinear to point. + One may use the routine 'point(trilinear)' to force the casting.</documentation></operator></asyxml>*/ + return point(tri); +} + +/*<asyxml><typedef type = "centerfunction" return = "real" params = "real, real, real"><code></asyxml>*/ +typedef real centerfunction(real, real, real);/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/TriangleCenterFunction.html"/></documentation></typedef></asyxml>*/ + +/*<asyxml><function type="trilinear" signature="trilinear(triangle,centerfunction,real,real,real)"><code></asyxml>*/ +trilinear trilinear(triangle t, centerfunction f, real a = t.a(), real b = t.b(), real c = t.c()) +{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/TriangleCenterFunction.html"/></documentation></function></asyxml>*/ + return trilinear(t, f(a, b, c), f(b, c, a), f(c, a, b)); +} + +/*<asyxml><function type="point" signature="symmedian(triangle)"><code></asyxml>*/ +point symmedian(triangle t) +{/*<asyxml></code><documentation>Return the symmedian point of 't'.</documentation></function></asyxml>*/ + point A, B, C; + real a = t.a(), b = t.b(), c = t.c(); + A = trilinear(t, 0, b, c); + B = trilinear(t, a, 0, c); + return intersectionpoint(line(t.A, A), line(t.B, B)); +} + +/*<asyxml><function type="point" signature="symmedian(side)"><code></asyxml>*/ +point symmedian(side side) +{/*<asyxml></code><documentation>The symmedian point on the side 'side'.</documentation></function></asyxml>*/ + triangle t = side.t; + int n = numarray[abs(side.n) - 1]; + if(n == 1) return trilinear(t, t.a(), t.b(), 0); + if(n == 2) return trilinear(t, 0, t.b(), t.c()); + return trilinear(t, t.a(), 0, t.c()); +} + +/*<asyxml><function type="line" signature="symmedian(vertex)"><code></asyxml>*/ +line symmedian(vertex V) +{/*<asyxml></code><documentation>Return the symmedian passing through 'V'.</documentation></function></asyxml>*/ + return line(point(V), symmedian(V.t)); +} + +/*<asyxml><function type="triangle" signature="cevian(triangle,point)"><code></asyxml>*/ +triangle cevian(triangle t, point P) +{/*<asyxml></code><documentation>Return the Cevian triangle with respect of 'P' + <url href = "http://mathworld.wolfram.com/CevianTriangle.html"/>.</documentation></function></asyxml>*/ + trilinear tri = trilinear(t, locate(P)); + point A = point(trilinear(t, 0, tri.b, tri.c)); + point B = point(trilinear(t, tri.a, 0, tri.c)); + point C = point(trilinear(t, tri.a, tri.b, 0)); + return triangle(A, B, C); +} + +/*<asyxml><function type="point" signature="cevian(side,point)"><code></asyxml>*/ +point cevian(side side, point P) +{/*<asyxml></code><documentation>Return the Cevian point on 'side' with respect of 'P'.</documentation></function></asyxml>*/ + triangle t = side.t; + trilinear tri = trilinear(t, locate(P)); + int[] s = tricoef(side); + return point(trilinear(t, s[0] * tri.a, s[1] * tri.b, s[2] * tri.c)); +} + +/*<asyxml><function type="line" signature="cevian(vertex,point)"><code></asyxml>*/ +line cevian(vertex V, point P) +{/*<asyxml></code><documentation>Return line passing through 'V' and its Cevian image with respect of 'P'.</documentation></function></asyxml>*/ + return line(point(V), cevian(opposite(V), P)); +} + +/*<asyxml><function type="point" signature="gergonne(triangle)"><code></asyxml>*/ +point gergonne(triangle t) +{/*<asyxml></code><documentation>Return the Gergonne point of 't'.</documentation></function></asyxml>*/ + real f(real a, real b, real c){return 1/(a * (b + c - a));} + return point(trilinear(t, f)); +} + +/*<asyxml><function type="point[]" signature="fermat(triangle)"><code></asyxml>*/ +point[] fermat(triangle t) +{/*<asyxml></code><documentation>Return the Fermat points of 't'.</documentation></function></asyxml>*/ + point[] P; + real A = t.alpha(), B = t.beta(), C = t.gamma(); + P.push(point(trilinear(t, 1/Sin(A + 60), 1/Sin(B + 60), 1/Sin(C + 60)))); + P.push(point(trilinear(t, 1/Sin(A - 60), 1/Sin(B - 60), 1/Sin(C - 60)))); + return P; +} + +/*<asyxml><function type="point" signature="isotomicconjugate(triangle,point)"><code></asyxml>*/ +point isotomicconjugate(triangle t, point M) +{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsotomicConjugate.html"/></documentation></function></asyxml>*/ + if(!inside(t.Path(), locate(M))) abort("isotomic: the point must be inside the triangle."); + trilinear tr = trilinear(t, M); + return point(trilinear(t, 1/(t.a()^2 * tr.a), 1/(t.b()^2 * tr.b), 1/(t.c()^2 * tr.c))); +} + +/*<asyxml><function type="line" signature="isotomic(vertex,point)"><code></asyxml>*/ +line isotomic(vertex V, point M) +{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsotomicConjugate.html"/>.</documentation></function></asyxml>*/ + side op = opposite(V); + return line(V, rotate(180, midpoint(op)) * cevian(op, M)); +} + +/*<asyxml><function type="point" signature="isotomic(side,point)"><code></asyxml>*/ +point isotomic(side side, point M) +{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsotomicConjugate.html"/></documentation></function></asyxml>*/ + return intersectionpoint(isotomic(opposite(side), M), side); +} + +/*<asyxml><function type="triangle" signature="isotomic(triangle,point)"><code></asyxml>*/ +triangle isotomic(triangle t, point M) +{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsotomicConjugate.html"/></documentation></function></asyxml>*/ + return triangle(isotomic(t.BC, M), isotomic(t.CA, M), isotomic(t.AB, M)); +} + +/*<asyxml><function type="point" signature="isogonalconjugate(triangle,point)"><code></asyxml>*/ +point isogonalconjugate(triangle t, point M) +{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsogonalConjugate.html"/></documentation></function></asyxml>*/ + trilinear tr = trilinear(t, M); + return point(trilinear(t, 1/tr.a, 1/tr.b, 1/tr.c)); +} + +/*<asyxml><function type="point" signature="isogonal(side,point)"><code></asyxml>*/ +point isogonal(side side, point M) +{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsogonalConjugate.html"/></documentation></function></asyxml>*/ + return cevian(side, isogonalconjugate(side.t, M)); +} + +/*<asyxml><function type="line" signature="isogonal(vertex,point)"><code></asyxml>*/ +line isogonal(vertex V, point M) +{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsogonalConjugate.html"/></documentation></function></asyxml>*/ + return line(V, isogonal(opposite(V), M)); +} + +/*<asyxml><function type="triangle" signature="isogonal(triangle,point)"><code></asyxml>*/ +triangle isogonal(triangle t, point M) +{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsogonalConjugate.html"/></documentation></function></asyxml>*/ + return triangle(isogonal(t.BC, M), isogonal(t.CA, M), isogonal(t.AB, M)); +} + +/*<asyxml><function type="triangle" signature="pedal(triangle,point)"><code></asyxml>*/ +triangle pedal(triangle t, point M) +{/*<asyxml></code><documentation>Return the pedal triangle of 'M' in 't'. + <url href = "http://mathworld.wolfram.com/PedalTriangle.html"/></documentation></function></asyxml>*/ + return triangle(projection(t.BC) * M, projection(t.AC) * M, projection(t.AB) * M); +} + +/*<asyxml><function type="triangle" signature="pedal(triangle,point)"><code></asyxml>*/ +line pedal(side side, point M) +{/*<asyxml></code><documentation>Return the pedal line of 'M' cutting 'side'. + <url href = "http://mathworld.wolfram.com/PedalTriangle.html"/></documentation></function></asyxml>*/ + return line(M, projection(side) * M); +} + +/*<asyxml><function type="triangle" signature="antipedal(triangle,point)"><code></asyxml>*/ +triangle antipedal(triangle t, point M) +{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/AntipedalTriangle.html"/></documentation></function></asyxml>*/ + trilinear Tm = trilinear(t, M); + real a = Tm.a, b = Tm.b, c = Tm.c; + real CA = Cos(t.alpha()), CB = Cos(t.beta()), CC = Cos(t.gamma()); + point A = trilinear(t, -(b + a * CC) * (c + a * CB), (c + a * CB) * (a + b * CC), (b + a * CC) * (a + c * CB)); + point B = trilinear(t, (c + b * CA) * (b + a * CC), -(c + b * CA) * (a + b * CC), (a + b * CC) * (b + c * CA)); + point C = trilinear(t, (b + c * CA) * (c + a * CB), (a + c * CB) * (c + b * CA), -(a + c * CB) * (b + c * CA)); + return triangle(A, B, C); +} + +/*<asyxml><function type="triangle" signature="extouch(triangle)"><code></asyxml>*/ +triangle extouch(triangle t) +{/*<asyxml></code><documentation>Return the extouch triangle of the triangle 't'. + The extouch triangle of 't' is the triangle formed by the points + of tangency of a triangle 't' with its excircles.</documentation></function></asyxml>*/ + point A, B, C; + real a = t.a(), b = t.b(), c = t.c(); + A = trilinear(t, 0, (a - b + c)/b, (a + b - c)/c); + B = trilinear(t, (-a + b + c)/a, 0, (a + b - c)/c); + C = trilinear(t, (-a + b + c)/a, (a - b + c)/b, 0); + return triangle(A, B, C); +} + +/*<asyxml><function type="triangle" signature="extouch(triangle)"><code></asyxml>*/ +triangle incentral(triangle t) +{/*<asyxml></code><documentation>Return the incentral triangle of the triangle 't'. + It is the triangle whose vertices are determined by the intersections of the + reference triangle's angle bisectors with the respective opposite sides.</documentation></function></asyxml>*/ + point A, B, C; + // real a = t.a(), b = t.b(), c = t.c(); + A = trilinear(t, 0, 1, 1); + B = trilinear(t, 1, 0, 1); + C = trilinear(t, 1, 1, 0); + return triangle(A, B, C); +} + +/*<asyxml><function type="triangle" signature="extouch(side)"><code></asyxml>*/ +triangle extouch(side side) +{/*<asyxml></code><documentation>Return the triangle formed by the points of tangency of the triangle referenced by 'side' with its excircles. + One vertex of the returned triangle is on the segment 'side'.</documentation></function></asyxml>*/ + triangle t = side.t; + transform p1 = projection((line)t.AB); + transform p2 = projection((line)t.AC); + transform p3 = projection((line)t.BC); + point EP = excenter(side); + return triangle(p3 * EP, p2 * EP, p1 * EP); +} + +/*<asyxml><function type="point" signature="bisectorpoint(side)"><code></asyxml>*/ +point bisectorpoint(side side) +{/*<asyxml></code><documentation>The intersection point of the angle bisector from the + opposite point of 'side' with the side 'side'.</documentation></function></asyxml>*/ + triangle t = side.t; + int n = numarray[abs(side.n) - 1]; + if(n == 1) return trilinear(t, 1, 1, 0); + if(n == 2) return trilinear(t, 0, 1, 1); + return trilinear(t, 1, 0, 1); +} + +/*<asyxml><function type="line" signature="bisector(vertex,real)"><code></asyxml>*/ +line bisector(vertex V, real angle = 0) +{/*<asyxml></code><documentation>Return the interior bisector passing through 'V' rotated by angle (in degrees) + around 'V'.</documentation></function></asyxml>*/ + return rotate(angle, point(V)) * line(point(V), incenter(V.t)); +} + +/*<asyxml><function type="line" signature="bisector(side)"><code></asyxml>*/ +line bisector(side side) +{/*<asyxml></code><documentation>Return the bisector of the line segment 'side'.</documentation></function></asyxml>*/ + return bisector(segment(side)); +} + +/*<asyxml><function type="point" signature="intouch(side)"><code></asyxml>*/ +point intouch(side side) +{/*<asyxml></code><documentation>The point of tangency on the side 'side' of its incircle.</documentation></function></asyxml>*/ + triangle t = side.t; + real a = t.a(), b = t.b(), c = t.c(); + int n = numarray[abs(side.n) - 1]; + if(n == 1) return trilinear(t, b * c/(-a + b + c), a * c/(a - b + c), 0); + if(n == 2) return trilinear(t, 0, a * c/(a - b + c), a * b/(a + b - c)); + return trilinear(t, b * c/(-a + b + c), 0, a * b/(a + b - c)); +} + +/*<asyxml><function type="triangle" signature="intouch(triangle)"><code></asyxml>*/ +triangle intouch(triangle t) +{/*<asyxml></code><documentation>Return the intouch triangle of the triangle 't'. + The intouch triangle of 't' is the triangle formed by the points + of tangency of a triangle 't' with its incircles.</documentation></function></asyxml>*/ + point A, B, C; + real a = t.a(), b = t.b(), c = t.c(); + A = trilinear(t, 0, a * c/(a - b + c), a * b/(a + b - c)); + B = trilinear(t, b * c/(-a + b + c), 0, a * b/(a + b - c)); + C = trilinear(t, b * c/(-a + b + c), a * c/(a - b + c), 0); + return triangle(A, B, C); +} + +/*<asyxml><function type="triangle" signature="tangential(triangle)"><code></asyxml>*/ +triangle tangential(triangle t) +{/*<asyxml></code><documentation>Return the tangential triangle of the triangle 't'. + The tangential triangle of 't' is the triangle formed by the lines + tangent to the circumcircle of the given triangle 't' at its vertices.</documentation></function></asyxml>*/ + point A, B, C; + real a = t.a(), b = t.b(), c = t.c(); + A = trilinear(t, -a, b, c); + B = trilinear(t, a, -b, c); + C = trilinear(t, a, b, -c); + return triangle(A, B, C); +} + +/*<asyxml><function type="triangle" signature="medial(triangle t)"><code></asyxml>*/ +triangle medial(triangle t) +{/*<asyxml></code><documentation>Return the triangle whose vertices are midpoints of the sides of 't'.</documentation></function></asyxml>*/ + return triangle(midpoint(t.BC), midpoint(t.AC), midpoint(t.AB)); +} + +/*<asyxml><function type="line" signature="median(vertex)"><code></asyxml>*/ +line median(vertex V) +{/*<asyxml></code><documentation>Return median from 'V'.</documentation></function></asyxml>*/ + return line(point(V), midpoint(segment(opposite(V)))); +} + +/*<asyxml><function type="line" signature="median(side)"><code></asyxml>*/ +line median(side side) +{/*<asyxml></code><documentation>Return median from the opposite vertex of 'side'.</documentation></function></asyxml>*/ + return median(opposite(side)); +} + +/*<asyxml><function type="triangle" signature="orthic(triangle)"><code></asyxml>*/ +triangle orthic(triangle t) +{/*<asyxml></code><documentation>Return the triangle whose vertices are endpoints of the altitudes from each of the vertices of 't'.</documentation></function></asyxml>*/ + return triangle(foot(t.BC), foot(t.AC), foot(t.AB)); +} + +/*<asyxml><function type="triangle" signature="symmedial(triangle)"><code></asyxml>*/ +triangle symmedial(triangle t) +{/*<asyxml></code><documentation>Return the symmedial triangle of 't'.</documentation></function></asyxml>*/ + point A, B, C; + real a = t.a(), b = t.b(), c = t.c(); + A = trilinear(t, 0, b, c); + B = trilinear(t, a, 0, c); + C = trilinear(t, a, b, 0); + return triangle(A, B, C); +} + +/*<asyxml><function type="triangle" signature="anticomplementary(triangle)"><code></asyxml>*/ +triangle anticomplementary(triangle t) +{/*<asyxml></code><documentation>Return the triangle which has the given triangle 't' as its medial triangle.</documentation></function></asyxml>*/ + real a = t.a(), b = t.b(), c = t.c(); + real ab = a * b, bc = b * c, ca = c * a; + point A = trilinear(t, -bc, ca, ab); + point B = trilinear(t, bc, -ca, ab); + point C = trilinear(t, bc, ca, -ab); + return triangle(A, B, C); +} + +/*<asyxml><function type="point[]" signature="intersectionpoints(triangle,line,bool)"><code></asyxml>*/ +point[] intersectionpoints(triangle t, line l, bool extended = false) +{/*<asyxml></code><documentation>Return the intersection points. + If 'extended' is true, the sides are lines else the sides are segments. + intersectionpoints(line, triangle, bool) is also defined.</documentation></function></asyxml>*/ + point[] OP; + void addpoint(point P) + { + if(defined(P)) { + bool exist = false; + for (int i = 0; i < OP.length; ++i) { + if(P == OP[i]) {exist = true; break;} + } + if(!exist) OP.push(P); + } + } + if(extended) { + for (int i = 1; i <= 3; ++i) { + addpoint(intersectionpoint(t.line(i), l)); + } + } else { + for (int i = 1; i <= 3; ++i) { + addpoint(intersectionpoint((segment)t.line(i), l)); + } + } + return OP; +} + +point[] intersectionpoints(line l, triangle t, bool extended = false) +{ + return intersectionpoints(t, l, extended); +} + +/*<asyxml><function type="vector" signature="dir(vertex)"><code></asyxml>*/ +vector dir(vertex V) +{/*<asyxml></code><documentation>The direction (towards the outside of the triangle) of the interior angle bisector of 'V'.</documentation></function></asyxml>*/ + triangle t = V.t; + if(V.n == 1) return vector(defaultcoordsys, (-dir(t.A--t.B, t.A--t.C))); + if(V.n == 2) return vector(defaultcoordsys, (-dir(t.B--t.A, t.B--t.C))); + return vector(defaultcoordsys, (-dir(t.C--t.A, t.C--t.B))); +} + +/*<asyxml><function type="void" signature="lvoid label(picture,Label,vertex,pair,real,pen,filltype)"><code></asyxml>*/ +void label(picture pic = currentpicture, Label L, vertex V, + pair align = dir(V), + real alignFactor = 1, + pen p = nullpen, filltype filltype = NoFill) +{/*<asyxml></code><documentation>Draw 'L' on picture 'pic' at vertex 'V' aligned by 'alignFactor * align'.</documentation></function></asyxml>*/ + label(pic, L, locate(point(V)), alignFactor * align, p, filltype); +} + +/*<asyxml><function type="void" signature="label(picture,Label,Label,Label,triangle,real,real,pen,filltype)"><code></asyxml>*/ +void label(picture pic = currentpicture, Label LA = "$A$", + Label LB = "$B$", Label LC = "$C$", + triangle t, + real alignAngle = 0, + real alignFactor = 1, + pen p = nullpen, filltype filltype = NoFill) +{/*<asyxml></code><documentation>Draw labels LA, LB and LC aligned in the rotated (by 'alignAngle' in degrees) direction + (towards the outside of the triangle) of the interior angle bisector of vertices. + One can individually modify the alignment by setting the Label parameter 'align'.</documentation></function></asyxml>*/ + Label lla = LA.copy(); + lla.align(lla.align, rotate(alignAngle) * locate(dir(t.VA))); + label(pic, LA, t.VA, align = lla.align.dir, alignFactor = alignFactor, p, filltype); + Label llb = LB.copy(); + llb.align(llb.align, rotate(alignAngle) * locate(dir(t.VB))); + label(pic, llb, t.VB, align = llb.align.dir, alignFactor = alignFactor, p, filltype); + Label llc = LC.copy(); + llc.align(llc.align, rotate(alignAngle) * locate(dir(t.VC))); + label(pic, llc, t.VC, align = llc.align.dir, alignFactor = alignFactor, p, filltype); +} + +/*<asyxml><function type="void" signature="show(picture,Label,Label,Label,Label,Label,Label,triangle,pen,filltype)"><code></asyxml>*/ +void show(picture pic = currentpicture, + Label LA = "$A$", Label LB = "$B$", Label LC = "$C$", + Label La = "$a$", Label Lb = "$b$", Label Lc = "$c$", + triangle t, pen p = currentpen, filltype filltype = NoFill) +{/*<asyxml></code><documentation>Draw triangle and labels of sides and vertices.</documentation></function></asyxml>*/ + pair a = locate(t.A), b = locate(t.B), c = locate(t.C); + draw(pic, a--b--c--cycle, p); + label(pic, LA, a, -dir(a--b, a--c), p, filltype); + label(pic, LB, b, -dir(b--a, b--c), p, filltype); + label(pic, LC, c, -dir(c--a, c--b), p, filltype); + pair aligna = I * unit(c - b), alignb = I * unit(c - a), alignc = I * unit(b - a); + pair mAB = locate(midpoint(t.AB)), mAC = locate(midpoint(t.AC)), mBC = locate(midpoint(t.BC)); + label(pic, La, b--c, align = rotate(dot(a - mBC, aligna) > 0 ? 180 :0) * aligna, p); + label(pic, Lb, a--c, align = rotate(dot(b - mAC, alignb) > 0 ? 180 :0) * alignb, p); + label(pic, Lc, a--b, align = rotate(dot(c - mAB, alignc) > 0 ? 180 :0) * alignc, p); +} + +/*<asyxml><function type="void" signature="draw(picture,triangle,pen,marker)"><code></asyxml>*/ +void draw(picture pic = currentpicture, triangle t, pen p = currentpen, marker marker = nomarker) +{/*<asyxml></code><documentation>Draw sides of the triangle 't' on picture 'pic' using pen 'p'.</documentation></function></asyxml>*/ + draw(pic, t.Path(), p, marker); +} + +/*<asyxml><function type="void" signature="draw(picture,triangle[],pen,marker)"><code></asyxml>*/ +void draw(picture pic = currentpicture, triangle[] t, pen p = currentpen, marker marker = nomarker) +{/*<asyxml></code><documentation>Draw sides of the triangles 't' on picture 'pic' using pen 'p'.</documentation></function></asyxml>*/ + for(int i = 0; i < t.length; ++i) draw(pic, t[i], p, marker); +} + +/*<asyxml><function type="void" signature="drawline(picture,triangle,pen)"><code></asyxml>*/ +void drawline(picture pic = currentpicture, triangle t, pen p = currentpen) +{/*<asyxml></code><documentation>Draw lines of the triangle 't' on picture 'pic' using pen 'p'.</documentation></function></asyxml>*/ + draw(t, p); + draw(pic, line(t.A, t.B), p); + draw(pic, line(t.A, t.C), p); + draw(pic, line(t.B, t.C), p); +} + +/*<asyxml><function type="void" signature="dot(picture,triangle,pen)"><code></asyxml>*/ +void dot(picture pic = currentpicture, triangle t, pen p = currentpen) +{/*<asyxml></code><documentation>Draw a dot at each vertex of 't'.</documentation></function></asyxml>*/ + dot(pic, t.A^^t.B^^t.C, p); +} +// *.......................TRIANGLES.......................* +// *=======================================================* + +// *=======================================================* +// *.......................INVERSIONS......................* +/*<asyxml><function type="point" signature="inverse(real k,point,point)"><code></asyxml>*/ +point inverse(real k, point A, point M) +{/*<asyxml></code><documentation>Return the inverse point of 'M' with respect to point A and inversion radius 'k'.</documentation></function></asyxml>*/ + return A + k/conj(M - A); +} + +/*<asyxml><function type="point" signature="radicalcenter(circle,circle)"><code></asyxml>*/ +point radicalcenter(circle c1, circle c2) +{/*<asyxml></code><documentation><url href = "http://fr.wikipedia.org/wiki/Puissance_d'un_point_par_rapport_%C3%A0_un_cercle"/></documentation></function></asyxml>*/ + point[] P = standardizecoordsys(c1.C, c2.C); + real k = c1.r^2 - c2.r^2; + pair C1 = locate(c1.C); + pair C2 = locate(c2.C); + pair oop = C2 - C1; + pair K = (abs(oop) == 0) ? + (infinity, infinity) : + midpoint(C1--C2) + 0.5 * k * oop/dot(oop, oop); + return point(P[0].coordsys, K/P[0].coordsys); +} + +/*<asyxml><function type="line" signature="radicalline(circle,circle)"><code></asyxml>*/ +line radicalline(circle c1, circle c2) +{/*<asyxml></code><documentation><url href = "http://fr.wikipedia.org/wiki/Puissance_d'un_point_par_rapport_%C3%A0_un_cercle"/></documentation></function></asyxml>*/ + if (c1.C == c2.C) abort("radicalline: the centers must be distinct"); + return perpendicular(radicalcenter(c1, c2), line(c1.C, c2.C)); +} + +/*<asyxml><function type="point" signature="radicalcenter(circle,circle,circle)"><code></asyxml>*/ +point radicalcenter(circle c1, circle c2, circle c3) +{/*<asyxml></code><documentation><url href = "http://fr.wikipedia.org/wiki/Puissance_d'un_point_par_rapport_%C3%A0_un_cercle"/></documentation></function></asyxml>*/ + return intersectionpoint(radicalline(c1, c2), radicalline(c1, c3)); +} + +/*<asyxml><struct signature="inversion"><code></asyxml>*/ +struct inversion +{/*<asyxml></code><documentation>http://mathworld.wolfram.com/Inversion.html</documentation></asyxml>*/ + point C; + real k; +}/*<asyxml></struct></asyxml>*/ + +/*<asyxml><function type="inversion" signature="inversion(real,point)"><code></asyxml>*/ +inversion inversion(real k, point C) +{/*<asyxml></code><documentation>Return the inversion with respect to 'C' having inversion radius 'k'.</documentation></function></asyxml>*/ + inversion oi; + oi.k = k; + oi.C = C; + return oi; +} +/*<asyxml><function type="inversion" signature="inversion(real,point)"><code></asyxml>*/ +inversion inversion(point C, real k) +{/*<asyxml></code><documentation>Return the inversion with respect to 'C' having inversion radius 'k'.</documentation></function></asyxml>*/ + return inversion(k, C); +} + +/*<asyxml><function type="inversion" signature="inversion(circle,circle)"><code></asyxml>*/ +inversion inversion(circle c1, circle c2, real sgn = 1) +{/*<asyxml></code><documentation>Return the inversion which transforms 'c1' to + . 'c2' and positive inversion radius if 'sgn > 0'; + . 'c2' and negative inversion radius if 'sgn < 0'; + . 'c1' and 'c2' to 'c2' if 'sgn = 0'.</documentation></function></asyxml>*/ + if(sgn == 0) { + point O = radicalcenter(c1, c2); + return inversion(O^c1, O); + } + real a = abs(c1.r/c2.r); + if(sgn > 0) { + point O = c1.C + a/abs(1 - a) * (c2.C - c1.C); + return inversion(a * abs(abs(O - c2.C)^2 - c2.r^2), O); + } + point O = c1.C + a/abs(1 + a) * (c2.C - c1.C); + return inversion(-a * abs(abs(O - c2.C)^2 - c2.r^2), O); +} + +/*<asyxml><function type="inversion" signature="inversion(circle,circle,circle)"><code></asyxml>*/ +inversion inversion(circle c1, circle c2, circle c3) +{/*<asyxml></code><documentation>Return the inversion which transform 'c1' to 'c1', 'c2' to 'c2' and 'c3' to 'c3'.</documentation></function></asyxml>*/ + point Rc = radicalcenter(c1, c2, c3); + return inversion(Rc, Rc^c1); +} + +circle operator cast(inversion i){return circle(i.C, sgn(i.k) * sqrt(abs(i.k)));} +/*<asyxml><function type="circle" signature="circle(inversion)"><code></asyxml>*/ +circle circle(inversion i) +{/*<asyxml></code><documentation>Return the inversion circle of 'i'.</documentation></function></asyxml>*/ + return i; +} + +inversion operator cast(circle c) +{ + return inversion(sgn(c.r) * c.r^2, c.C); +} +/*<asyxml><function type="inversion" signature="inversion(circle)"><code></asyxml>*/ +inversion inversion(circle c) +{/*<asyxml></code><documentation>Return the inversion represented by the circle of 'c'.</documentation></function></asyxml>*/ + return c; +} + +/*<asyxml><operator type = "point" signature="*(inversion,point)"><code></asyxml>*/ +point operator *(inversion i, point P) +{/*<asyxml></code><documentation>Provide inversion * point.</documentation></operator></asyxml>*/ + return inverse(i.k, i.C, P); +} + +void lineinversion() +{ + warning("lineinversion", "the inversion of the line is not a circle. +The returned circle has an infinite radius, circle.l has been set."); +} + + +/*<asyxml><function type="circle" signature="inverse(real,point,line)"><code></asyxml>*/ +circle inverse(real k, point A, line l) +{/*<asyxml></code><documentation>Return the inverse circle of 'l' with + respect to point 'A' and inversion radius 'k'.</documentation></function></asyxml>*/ + if(A @ l) { + lineinversion(); + circle C = circle(A, infinity); + C.l = l; + return C; + } + point Ap = inverse(k, A, l.A), Bp = inverse(k, A, l.B); + return circle(A, Ap, Bp); +} + +/*<asyxml><operator type = "circle" signature="*(inversion,line)"><code></asyxml>*/ +circle operator *(inversion i, line l) +{/*<asyxml></code><documentation>Provide inversion * line for lines that don't pass through the inversion center.</documentation></operator></asyxml>*/ + return inverse(i.k, i.C, l); +} + +/*<asyxml><function type="circle" signature="inverse(real,point,circle)"><code></asyxml>*/ +circle inverse(real k, point A, circle c) +{/*<asyxml></code><documentation>Return the inverse circle of 'c' with + respect to point A and inversion radius 'k'.</documentation></function></asyxml>*/ + if(degenerate(c)) return inverse(k, A, c.l); + if(A @ c) { + lineinversion(); + point M = rotate(180, c.C) * A, Mp = rotate(90, c.C) * A; + circle oc = circle(A, infinity); + oc.l = line(inverse(k, A, M), inverse(k, A, Mp)); + return oc; + } + point[] P = standardizecoordsys(A, c.C); + real s = k/((P[1].x - P[0].x)^2 + (P[1].y - P[0].y)^2 - c.r^2); + return circle(P[0] + s * (P[1]-P[0]), abs(s) * c.r); +} + +/*<asyxml><operator type = "circle" signature="*(inversion,circle)"><code></asyxml>*/ +circle operator *(inversion i, circle c) +{/*<asyxml></code><documentation>Provide inversion * circle.</documentation></operator></asyxml>*/ + return inverse(i.k, i.C, c); +} +// *.......................INVERSIONS......................* +// *=======================================================* + +// *=======================================================* +// *........................FOOTER.........................* +/*<asyxml><function type="point[]" signature="intersectionpoints(line,circle)"><code></asyxml>*/ +point[] intersectionpoints(line l, circle c) +{/*<asyxml></code><documentation>Note that the line 'l' may be a segment by casting. + intersectionpoints(circle, line) is also defined.</documentation></function></asyxml>*/ + if(degenerate(c)) return new point[]{intersectionpoint(l, c.l)}; + point[] op; + coordsys R = samecoordsys(l.A, c.C) ? + l.A.coordsys : defaultcoordsys; + coordsys Rp = defaultcoordsys; + circle cc = circle(changecoordsys(Rp, c.C), c.r); + point proj = projection(l) * c.C; + if(proj @ cc) { // The line is a tangente of the circle. + if(proj @ l) op.push(proj);// line may be a segement... + } else { + coordsys Rc = cartesiansystem(c.C, (1, 0), (0, 1)); + line ll = changecoordsys(Rc, l); + pair[] P = intersectionpoints(ll.A.coordinates, ll.B.coordinates, + 1, 0, 1, 0, 0, -c.r^2); + for (int i = 0; i < P.length; ++i) { + point inter = changecoordsys(R, point(Rc, P[i])); + if(inter @ l) op.push(inter); + } + } + return op; +} + +point[] intersectionpoints(circle c, line l) +{ + return intersectionpoints(l, c); +} + +/*<asyxml><function type="point[]" signature="intersectionpoints(line,ellipse)"><code></asyxml>*/ +point[] intersectionpoints(line l, ellipse el) +{/*<asyxml></code><documentation>Note that the line 'l' may be a segment by casting. + intersectionpoints(ellipse, line) is also defined.</documentation></function></asyxml>*/ + if(el.e == 0) return intersectionpoints(l, (circle)el); + if(degenerate(el)) return new point[]{intersectionpoint(l, el.l)}; + point[] op; + coordsys R = samecoordsys(l.A, el.C) ? l.A.coordsys : defaultcoordsys; + coordsys Rp = defaultcoordsys; + line ll = changecoordsys(Rp, l); + ellipse ell = changecoordsys(Rp, el); + circle C = circle(ell.C, ell.a); + point[] Ip = intersectionpoints(ll, C); + if (Ip.length > 0 && + (perpendicular(ll, line(ell.F1, Ip[0])) || + perpendicular(ll, line(ell.F2, Ip[0])))) { + // http://www.mathcurve.com/courbes2d/ellipse/ellipse.shtml + // Définition tangentielle par antipodaire de cercle. + // 'l' is a tangent of 'el' + transform t = scale(el.a/el.b, el.F1, el.F2, el.C, rotate(90, el.C) * el.F1); + point inter = inverse(t) * intersectionpoints(C, t * ll)[0]; + if(inter @ l) op.push(inter); + } else { + coordsys Rc = canonicalcartesiansystem(el); + line ll = changecoordsys(Rc, l); + pair[] P = intersectionpoints(ll.A.coordinates, ll.B.coordinates, + 1/el.a^2, 0, 1/el.b^2, 0, 0, -1); + for (int i = 0; i < P.length; ++i) { + point inter = changecoordsys(R, point(Rc, P[i])); + if(inter @ l) op.push(inter); + } + } + return op; +} + +point[] intersectionpoints(ellipse el, line l) +{ + return intersectionpoints(l, el); +} + +/*<asyxml><function type="point[]" signature="intersectionpoints(line,parabola)"><code></asyxml>*/ +point[] intersectionpoints(line l, parabola p) +{/*<asyxml></code><documentation>Note that the line 'l' may be a segment by casting. + intersectionpoints(parabola, line) is also defined.</documentation></function></asyxml>*/ + point[] op; + coordsys R = coordsys(p); + bool tgt = false; + line ll = changecoordsys(R, l), + lv = parallel(p.V, p.D); + point M = intersectionpoint(lv, ll), tgtp; + if(finite(M)) {// Test if 'l' is tangent to 'p' + line l1 = bisector(line(M, p.F)); + line l2 = rotate(90, M) * lv; + point P = intersectionpoint(l1, l2); + tgtp = rotate(180, P) * p.F; + tgt = (tgtp @ l); + } + if(tgt) { + if(tgtp @ l) op.push(tgtp); + } else { + real[] eq = changecoordsys(defaultcoordsys, equation(p)).a; + pair[] tp = intersectionpoints(locate(l.A), locate(l.B), eq); + point inter; + for (int i = 0; i < tp.length; ++i) { + inter = point(R, tp[i]/R); + if(inter @ l) op.push(inter); + } + } + return op; +} + +point[] intersectionpoints(parabola p, line l) +{ + return intersectionpoints(l, p); +} + +/*<asyxml><function type="point[]" signature="intersectionpoints(line,hyperbola)"><code></asyxml>*/ +point[] intersectionpoints(line l, hyperbola h) +{/*<asyxml></code><documentation>Note that the line 'l' may be a segment by casting. + intersectionpoints(hyperbola, line) is also defined.</documentation></function></asyxml>*/ + point[] op; + coordsys R = coordsys(h); + point A = intersectionpoint(l, h.A1), B = intersectionpoint(l, h.A2); + point M = midpoint(segment(A, B)); + bool tgt = M @ h; + if(tgt) { + if(M @ l) op.push(M); + } else { + real[] eq = changecoordsys(defaultcoordsys, equation(h)).a; + pair[] tp = intersectionpoints(locate(l.A), locate(l.B), eq); + point inter; + for (int i = 0; i < tp.length; ++i) { + inter = point(R, tp[i]/R); + if(inter @ l) op.push(inter); + } + } + return op; +} + +point[] intersectionpoints(hyperbola h, line l) +{ + return intersectionpoints(l, h); +} + +/*<asyxml><function type="point[]" signature="intersectionpoints(line,conic)"><code></asyxml>*/ +point[] intersectionpoints(line l, conic co) +{/*<asyxml></code><documentation>Note that the line 'l' may be a segment by casting. + intersectionpoints(conic, line) is also defined.</documentation></function></asyxml>*/ + point[] op; + if(co.e < 1) op = intersectionpoints((ellipse)co, l); + else + if(co.e == 1) op = intersectionpoints((parabola)co, l); + else op = intersectionpoints((hyperbola)co, l); + return op; +} + +point[] intersectionpoints(conic co, line l) +{ + return intersectionpoints(l, co); +} + +/*<asyxml><function type="point[]" signature="intersectionpoints(conic,conic)"><code></asyxml>*/ +point[] intersectionpoints(conic co1, conic co2) +{/*<asyxml></code><documentation>Return the intersection points of the two conics.</documentation></function></asyxml>*/ + if(degenerate(co1)) return intersectionpoints(co1.l[0], co2); + if(degenerate(co2)) return intersectionpoints(co1, co2.l[0]); + return intersectionpoints(equation(co1), equation(co2)); +} + +/*<asyxml><function type="point[]" signature="intersectionpoints(triangle,conic,bool)"><code></asyxml>*/ +point[] intersectionpoints(triangle t, conic co, bool extended = false) +{/*<asyxml></code><documentation>Return the intersection points. + If 'extended' is true, the sides are lines else the sides are segments. + intersectionpoints(conic, triangle, bool) is also defined.</documentation></function></asyxml>*/ + if(degenerate(co)) return intersectionpoints(t, co.l[0], extended); + point[] OP; + void addpoint(point P[]) + { + for (int i = 0; i < P.length; ++i) { + if(defined(P[i])) { + bool exist = false; + for (int j = 0; j < OP.length; ++j) { + if(P[i] == OP[j]) {exist = true; break;} + } + if(!exist) OP.push(P[i]); + }}} + if(extended) { + for (int i = 1; i <= 3; ++i) { + addpoint(intersectionpoints(t.line(i), co)); + } + } else { + for (int i = 1; i <= 3; ++i) { + addpoint(intersectionpoints((segment)t.line(i), co)); + } + } + return OP; +} + +point[] intersectionpoints(conic co, triangle t, bool extended = false) +{ + return intersectionpoints(t, co, extended); +} + +/*<asyxml><function type="point[]" signature="intersectionpoints(ellipse,ellipse)"><code></asyxml>*/ +point[] intersectionpoints(ellipse a, ellipse b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + // if(degenerate(a)) return intersectionpoints(a.l, b); + // if(degenerate(b)) return intersectionpoints(a, b.l);; + return intersectionpoints((conic)a, (conic)b); +} +/*<asyxml><function type="point[]" signature="intersectionpoints(ellipse,circle)"><code></asyxml>*/ +point[] intersectionpoints(ellipse a, circle b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + // if(degenerate(a)) return intersectionpoints(a.l, b); + // if(degenerate(b)) return intersectionpoints(a, b.l);; + return intersectionpoints((conic)a, (conic)b); +} +/*<asyxml><function type="point[]" signature="intersectionpoints(circle,ellipse)"><code></asyxml>*/ +point[] intersectionpoints(circle a, ellipse b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return intersectionpoints(b, a); +} +/*<asyxml><function type="point[]" signature="intersectionpoints(ellipse,parabola)"><code></asyxml>*/ +point[] intersectionpoints(ellipse a, parabola b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + // if(degenerate(a)) return intersectionpoints(a.l, b); + return intersectionpoints((conic)a, (conic)b); +} +/*<asyxml><function type="point[]" signature="intersectionpoints(parabola,ellipse)"><code></asyxml>*/ +point[] intersectionpoints(parabola a, ellipse b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return intersectionpoints(b, a); +} +/*<asyxml><function type="point[]" signature="intersectionpoints(ellipse,hyperbola)"><code></asyxml>*/ +point[] intersectionpoints(ellipse a, hyperbola b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + // if(degenerate(a)) return intersectionpoints(a.l, b); + return intersectionpoints((conic)a, (conic)b); +} +/*<asyxml><function type="point[]" signature="intersectionpoints(hyperbola,ellipse)"><code></asyxml>*/ +point[] intersectionpoints(hyperbola a, ellipse b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return intersectionpoints(b, a); +} + +/*<asyxml><function type="point[]" signature="intersectionpoints(circle,parabola)"><code></asyxml>*/ +point[] intersectionpoints(circle a, parabola b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return intersectionpoints((conic)a, (conic)b); +} +/*<asyxml><function type="point[]" signature="intersectionpoints(parabola,circle)"><code></asyxml>*/ +point[] intersectionpoints(parabola a, circle b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return intersectionpoints((conic)a, (conic)b); +} +/*<asyxml><function type="point[]" signature="intersectionpoints(circle,hyperbola)"><code></asyxml>*/ +point[] intersectionpoints(circle a, hyperbola b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return intersectionpoints((conic)a, (conic)b); +} +/*<asyxml><function type="point[]" signature="intersectionpoints(hyperbola,circle)"><code></asyxml>*/ +point[] intersectionpoints(hyperbola a, circle b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return intersectionpoints((conic)a, (conic)b); +} + +/*<asyxml><function type="point[]" signature="intersectionpoints(parabola,parabola)"><code></asyxml>*/ +point[] intersectionpoints(parabola a, parabola b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return intersectionpoints((conic)a, (conic)b); +} +/*<asyxml><function type="point[]" signature="intersectionpoints(parabola,hyperbola)"><code></asyxml>*/ +point[] intersectionpoints(parabola a, hyperbola b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return intersectionpoints((conic)a, (conic)b); +} +/*<asyxml><function type="point[]" signature="intersectionpoints(hyperbola,parabola)"><code></asyxml>*/ +point[] intersectionpoints(hyperbola a, parabola b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return intersectionpoints((conic)a, (conic)b); +} +/*<asyxml><function type="point[]" signature="intersectionpoints(hyperbola,hyperbola)"><code></asyxml>*/ +point[] intersectionpoints(hyperbola a, hyperbola b) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + return intersectionpoints((conic)a, (conic)b); +} + +/*<asyxml><function type="point[]" signature="intersectionpoints(circle,circle)"><code></asyxml>*/ +point[] intersectionpoints(circle c1, circle c2) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + if(degenerate(c1)) + return degenerate(c2) ? + new point[]{intersectionpoint(c1.l, c2.l)} : intersectionpoints(c1.l, c2); + if(degenerate(c2)) return intersectionpoints(c1, c2.l); + return (c1.C == c2.C) ? + new point[] : + intersectionpoints(radicalline(c1, c2), c1); +} + +/*<asyxml><function type="line" signature="tangent(circle,abscissa)"><code></asyxml>*/ +line tangent(circle c, abscissa x) +{/*<asyxml></code><documentation>Return the tangent of 'c' at 'point(c, x)'.</documentation></function></asyxml>*/ + if(c.r == 0) abort("tangent: a circle with a radius equals zero has no tangent."); + point M = point(c, x); + return line(rotate(90, M) * c.C, M); +} + +/*<asyxml><function type="line[]" signature="tangents(circle,point)"><code></asyxml>*/ +line[] tangents(circle c, point M) +{/*<asyxml></code><documentation>Return the tangents of 'c' passing through 'M'.</documentation></function></asyxml>*/ + line[] ol; + if(inside(c, M)) return ol; + if(M @ c) { + ol.push(tangent(c, relabscissa(c, M))); + } else { + circle cc = circle(c.C, M); + point[] inter = intersectionpoints(c, cc); + for (int i = 0; i < inter.length; ++i) + ol.push(tangents(c, inter[i])[0]); + } + return ol; +} + +/*<asyxml><function type="point" signature="point(circle,point)"><code></asyxml>*/ +point point(circle c, point M) +{/*<asyxml></code><documentation>Return the intersection point of 'c' + with the half-line '[c.C M)'.</documentation></function></asyxml>*/ + return intersectionpoints(c, line(c.C, false, M))[0]; +} + +/*<asyxml><function type="line" signature="tangent(circle,point)"><code></asyxml>*/ +line tangent(circle c, point M) +{/*<asyxml></code><documentation>Return the tangent of 'c' at the + intersection point of the half-line'[c.C M)'.</documentation></function></asyxml>*/ + return tangents(c, point(c, M))[0]; +} + +/*<asyxml><function type="point" signature="point(circle,explicit vector)"><code></asyxml>*/ +point point(circle c, explicit vector v) +{/*<asyxml></code><documentation>Return the intersection point of 'c' + with the half-line '[c.C v)'.</documentation></function></asyxml>*/ + return point(c, c.C + v); +} + +/*<asyxml><function type="line" signature="tangent(circle,explicit vector)"><code></asyxml>*/ +line tangent(circle c, explicit vector v) +{/*<asyxml></code><documentation>Return the tangent of 'c' at the + point M so that vec(c.C M) is collinear to 'v' with the same sense.</documentation></function></asyxml>*/ + line ol = tangent(c, c.C + v); + return dot(ol.v, v) > 0 ? ol : reverse(ol); +} + +/*<asyxml><function type="line" signature="tangent(ellipse,abscissa)"><code></asyxml>*/ +line tangent(ellipse el, abscissa x) +{/*<asyxml></code><documentation>Return the tangent of 'el' at 'point(el, x)'.</documentation></function></asyxml>*/ + point M = point(el, x); + line l1 = line(el.F1, M); + line l2 = line(el.F2, M); + line ol = (l1 == l2) ? perpendicular(M, l1) : bisector(l1, l2, 90, false); + return ol; +} + +/*<asyxml><function type="line[]" signature="tangents(ellipse,point)"><code></asyxml>*/ +line[] tangents(ellipse el, point M) +{/*<asyxml></code><documentation>Return the tangents of 'el' passing through 'M'.</documentation></function></asyxml>*/ + line[] ol; + if(inside(el, M)) return ol; + if(M @ el) { + ol.push(tangent(el, relabscissa(el, M))); + } else { + point Mp = samecoordsys(M, el.F2) ? + M : changecoordsys(el.F2.coordsys, M); + circle c = circle(Mp, abs(el.F1 - Mp)); + circle cc = circle(el.F2, 2 * el.a); + point[] inter = intersectionpoints(c, cc); + for (int i = 0; i < inter.length; ++i) { + line tl = line(inter[i], el.F2, false); + point[] P = intersectionpoints(tl, el); + ol.push(line(Mp, P[0])); + } + } + return ol; +} + +/*<asyxml><function type="line" signature="tangent(parabola,abscissa)"><code></asyxml>*/ +line tangent(parabola p, abscissa x) +{/*<asyxml></code><documentation>Return the tangent of 'p' at 'point(p, x)' (use the Wells method).</documentation></function></asyxml>*/ + line lt = rotate(90, p.V) * line(p.V, p.F); + point P = point(p, x); + if(P == p.V) return lt; + point M = midpoint(segment(P, p.F)); + line l = rotate(90, M) * line(P, p.F); + return line(P, projection(lt) * M); +} + +/*<asyxml><function type="line[]" signature="tangents(parabola,point)"><code></asyxml>*/ +line[] tangents(parabola p, point M) +{/*<asyxml></code><documentation>Return the tangent of 'p' at 'M' (use the Wells method).</documentation></function></asyxml>*/ + line[] ol; + if(inside(p, M)) return ol; + if(M @ p) { + ol.push(tangent(p, angabscissa(p, M))); + } + else { + point Mt = changecoordsys(coordsys(p), M); + circle c = circle(Mt, p.F); + line l = rotate(90, p.V) * line(p.V, p.F); + point[] R = intersectionpoints(l, c); + for (int i = 0; i < R.length; ++i) { + ol.push(line(Mt, R[i])); + } + // An other method: http://www.du.edu/~jcalvert/math/parabola.htm + // point[] R = intersectionpoints(p.directrix, c); + // for (int i = 0; i < R.length; ++i) { + // ol.push(bisector(segment(p.F, R[i]))); + // } + } + return ol; +} + +/*<asyxml><function type="line" signature="tangent(hyperbola,abscissa)"><code></asyxml>*/ +line tangent(hyperbola h, abscissa x) +{/*<asyxml></code><documentation>Return the tangent of 'h' at 'point(p, x)'.</documentation></function></asyxml>*/ + point M = point(h, x); + line ol = bisector(line(M, h.F1), line(M, h.F2)); + if(sameside(h.F1, h.F2, ol) || ol == line(h.F1, h.F2)) ol = rotate(90, M) * ol; + return ol; +} + +/*<asyxml><function type="line[]" signature="tangents(hyperbola,point)"><code></asyxml>*/ +line[] tangents(hyperbola h, point M) +{/*<asyxml></code><documentation>Return the tangent of 'h' at 'M'.</documentation></function></asyxml>*/ + line[] ol; + if(M @ h) { + ol.push(tangent(h, angabscissa(h, M, fromCenter))); + } else { + coordsys cano = canonicalcartesiansystem(h); + bqe bqe = changecoordsys(cano, equation(h)); + real a = abs(1/(bqe.a[5] * bqe.a[0])), b = abs(1/(bqe.a[5] * bqe.a[2])); + point Mp = changecoordsys(cano, M); + real x0 = Mp.x, y0 = Mp.y; + if(abs(x0) > epsgeo) { + real c0 = a * y0^2/(b * x0)^2 - 1/b, + c1 = 2 * a * y0/(b * x0^2), c2 = a/x0^2 - 1; + real[] sol = quadraticroots(c0, c1, c2); + for (real y:sol) { + point tmp = changecoordsys(coordsys(h), point(cano, (a * (1 + y * y0/b)/x0, y))); + ol.push(line(M, tmp)); + } + } else if(abs(y0) > epsgeo) { + real y = -b/y0, x = sqrt(a * (1 + b/y0^2)); + ol.push(line(M, changecoordsys(coordsys(h), point(cano, (x, y))))); + ol.push(line(M, changecoordsys(coordsys(h), point(cano, (-x, y))))); + }} + return ol; +} + +/*<asyxml><function type="point[]" signature="intersectionpoints(conic,arc)"><code></asyxml>*/ +point[] intersectionpoints(conic co, arc a) +{/*<asyxml></code><documentation>intersectionpoints(arc, circle) is also defined.</documentation></function></asyxml>*/ + point[] op; + point[] tp = intersectionpoints(co, (conic)a.el); + for (int i = 0; i < tp.length; ++i) + if(tp[i] @ a) op.push(tp[i]); + return op; +} + +point[] intersectionpoints(arc a, conic co) +{ + return intersectionpoints(co, a); +} + +/*<asyxml><function type="point[]" signature="intersectionpoints(arc,arc)"><code></asyxml>*/ +point[] intersectionpoints(arc a1, arc a2) +{/*<asyxml></code><documentation></documentation></function></asyxml>*/ + point[] op; + point[] tp = intersectionpoints(a1.el, a2.el); + for (int i = 0; i < tp.length; ++i) + if(tp[i] @ a1 && tp[i] @ a2) op.push(tp[i]); + return op; +} + + +/*<asyxml><function type="point[]" signature="intersectionpoints(line,arc)"><code></asyxml>*/ +point[] intersectionpoints(line l, arc a) +{/*<asyxml></code><documentation>intersectionpoints(arc, line) is also defined.</documentation></function></asyxml>*/ + point[] op; + point[] tp = intersectionpoints(a.el, l); + for (int i = 0; i < tp.length; ++i) + if(tp[i] @ a && tp[i] @ l) op.push(tp[i]); + return op; +} + +point[] intersectionpoints(arc a, line l) +{ + return intersectionpoints(l, a); +} + +/*<asyxml><function type="point" signature="arcsubtendedcenter(point,point,real)"><code></asyxml>*/ +point arcsubtendedcenter(point A, point B, real angle) +{/*<asyxml></code><documentation>Return the center of the arc retuned + by the 'arcsubtended' routine.</documentation></function></asyxml>*/ + point OM; + point[] P = standardizecoordsys(A, B); + angle = angle%(sgnd(angle) * 180); + line bis = bisector(P[0], P[1]); + line AB = line(P[0], P[1]); + return intersectionpoint(bis, rotate(90 - angle, A) * AB); +} + +/*<asyxml><function type="arc" signature="arcsubtended(point,point,real)"><code></asyxml>*/ +arc arcsubtended(point A, point B, real angle) +{/*<asyxml></code><documentation>Return the arc circle from which the segment AB is saw with + the angle 'angle'. + If the point 'M' is on this arc, the oriented angle (MA, MB) is + equal to 'angle'.</documentation></function></asyxml>*/ + point[] P = standardizecoordsys(A, B); + line AB = line(P[0], P[1]); + angle = angle%(sgnd(angle) * 180); + point C = arcsubtendedcenter(P[0], P[1], angle); + real BC = degrees(B - C)%360; + real AC = degrees(A - C)%360; + return arc(circle(C, abs(B - C)), BC, AC, angle > 0 ? CCW : CW); +} + +/*<asyxml><function type="arc" signature="arccircle(point,point,point)"><code></asyxml>*/ +arc arccircle(point A, point M, point B) +{/*<asyxml></code><documentation>Return the CCW arc circle 'AB' passing through 'M'.</documentation></function></asyxml>*/ + circle tc = circle(A, M, B); + real a = degrees(A - tc.C); + real b = degrees(B - tc.C); + real m = degrees(M - tc.C); + + arc oa = arc(tc, a, b); + // TODO : use cross product to determine CWW or CW + if (!(M @ oa)) { + oa.direction = !oa.direction; + } + + return oa; +} + +/*<asyxml><function type="arc" signature="arc(ellipse,abscissa,abscissa,bool)"><code></asyxml>*/ +arc arc(ellipse el, explicit abscissa x1, explicit abscissa x2, bool direction = CCW) +{/*<asyxml></code><documentation>Return the arc from 'point(c, x1)' to 'point(c, x2)' in the direction 'direction'.</documentation></function></asyxml>*/ + real a = degrees(point(el, x1) - el.C); + real b = degrees(point(el, x2) - el.C); + arc oa = arc(el, a - el.angle, b - el.angle, fromCenter, direction); + return oa; +} + +/*<asyxml><function type="arc" signature="arc(ellipse,point,point,bool)"><code></asyxml>*/ +arc arc(ellipse el, point M, point N, bool direction = CCW) +{/*<asyxml></code><documentation>Return the arc from 'M' to 'N' in the direction 'direction'. + The points 'M' and 'N' must belong to the ellipse 'el'.</documentation></function></asyxml>*/ + return arc(el, relabscissa(el, M), relabscissa(el, N), direction); +} + +/*<asyxml><function type="arc" signature="arccircle(point,point,real,bool)"><code></asyxml>*/ +arc arccircle(point A, point B, real angle, bool direction = CCW) +{/*<asyxml></code><documentation>Return the arc circle centered on A + from B to rotate(angle, A) * B in the direction 'direction'.</documentation></function></asyxml>*/ + point M = rotate(angle, A) * B; + return arc(circle(A, abs(A - B)), B, M, direction); +} + +/*<asyxml><function type="arc" signature="arc(explicit arc,abscissa,abscissa)"><code></asyxml>*/ +arc arc(explicit arc a, abscissa x1, abscissa x2) +{/*<asyxml></code><documentation>Return the arc from 'point(a, x1)' to 'point(a, x2)' traversed in the direction of the arc direction.</documentation></function></asyxml>*/ + real a1 = angabscissa(a.el, point(a, x1), a.polarconicroutine).x; + real a2 = angabscissa(a.el, point(a, x2), a.polarconicroutine).x; + return arc(a.el, a1, a2, a.polarconicroutine, a.direction); +} + +/*<asyxml><function type="arc" signature="arc(explicit arc,point,point)"><code></asyxml>*/ +arc arc(explicit arc a, point M, point N) +{/*<asyxml></code><documentation>Return the arc from 'M' to 'N'. + The points 'M' and 'N' must belong to the arc 'a'.</documentation></function></asyxml>*/ + return arc(a, relabscissa(a, M), relabscissa(a, N)); +} + +/*<asyxml><function type="arc" signature="inverse(real,point,segment)"><code></asyxml>*/ +arc inverse(real k, point A, segment s) +{/*<asyxml></code><documentation>Return the inverse arc circle of 's' + with respect to point A and inversion radius 'k'.</documentation></function></asyxml>*/ + point Ap = inverse(k, A, s.A), Bp = inverse(k, A, s.B), + M = inverse(k, A, midpoint(s)); + return arccircle(Ap, M, Bp); +} + +/*<asyxml><operator type = "arc" signature="*(inversion,segment)"><code></asyxml>*/ +arc operator *(inversion i, segment s) +{/*<asyxml></code><documentation>Provide + inversion * segment.</documentation></operator></asyxml>*/ + return inverse(i.k, i.C, s); +} + +/*<asyxml><operator type = "path" signature="*(inversion,triangle)"><code></asyxml>*/ +path operator *(inversion i, triangle t) +{/*<asyxml></code><documentation>Provide inversion * triangle.</documentation></operator></asyxml>*/ + return (path)(i * segment(t.AB))-- + (path)(i * segment(t.BC))-- + (path)(i * segment(t.CA))&cycle; +} + +/*<asyxml><function type="path" signature="compassmark(pair,pair,real,real)"><code></asyxml>*/ +path compassmark(pair O, pair A, real position, real angle = 10) +{/*<asyxml></code><documentation>Return an arc centered on O with the angle 'angle' so that the position + of 'A' on this arc makes an angle 'position * angle'.</documentation></function></asyxml>*/ + real a = degrees(A - O); + real pa = (a - position * angle)%360, + pb = (a - (position - 1) * angle)%360; + real t1 = intersect(unitcircle, (0, 0)--2 * dir(pa))[0]; + real t2 = intersect(unitcircle, (0, 0)--2 * dir(pb))[0]; + int n = length(unitcircle); + if(t1 >= t2) t1 -= n; + return shift(O) * scale(abs(O - A)) * subpath(unitcircle, t1, t2); +} + +/*<asyxml><function type="line" signature="tangent(explicit arc,abscissa)"><code></asyxml>*/ +line tangent(explicit arc a, abscissa x) +{/*<asyxml></code><documentation>Return the tangent of 'a' at 'point(a, x)'.</documentation></function></asyxml>*/ + abscissa ag = angabscissa(a, point(a, x)); + return tangent(a.el, ag + a.angle1 + (a.el.e == 0 ? a.angle0 : 0)); +} + +/*<asyxml><function type="line" signature="tangent(explicit arc,point)"><code></asyxml>*/ +line tangent(explicit arc a, point M) +{/*<asyxml></code><documentation>Return the tangent of 'a' at 'M'. + The points 'M' must belong to the arc 'a'.</documentation></function></asyxml>*/ + return tangent(a, angabscissa(a, M)); +} + +// *=======================================================* +// *.......Routines for compatibility with original geometry module........* + +path square(pair z1, pair z2) +{ + pair v = z2 - z1; + pair z3 = z2 + I * v; + pair z4 = z3 - v; + return z1--z2--z3--z4--cycle; +} + +// Draw a perpendicular symbol at z aligned in the direction align +// relative to the path z--z + dir. +void perpendicular(picture pic = currentpicture, pair z, pair align, + pair dir = E, real size = 0, pen p = currentpen, + margin margin = NoMargin, filltype filltype = NoFill) +{ + perpendicularmark(pic, (point) z, align, dir, size, p, margin, filltype); +} + + +// Draw a perpendicular symbol at z aligned in the direction align +// relative to the path z--z + dir(g, 0) +void perpendicular(picture pic = currentpicture, pair z, pair align, path g, + real size = 0, pen p = currentpen, margin margin = NoMargin, + filltype filltype = NoFill) +{ + perpendicularmark(pic, (point) z, align, dir(g, 0), size, p, margin, filltype); +} + +// Return an interior arc BAC of triangle ABC, given a radius r > 0. +// If r < 0, return the corresponding exterior arc of radius |r|. +path arc(explicit pair B, explicit pair A, explicit pair C, real r) +{ + return arc(A, r, degrees(B - A), degrees(C - A)); +} + +// *.......End of compatibility routines........* +// *=======================================================* + +// *........................FOOTER.........................* +// *=======================================================* |