public static Circle2d[] CircleCircleLine(Circle2d ci1, Circle2d ci2, Line2d li) { // see http://www.arcenciel.co.uk/geometry/ for explanation List <Circle2d> result = null; double a1, b1, c1, t, r2, r3, a, b, c, u, s; double A, B, C, xc, yc; //transform input so that c1 is at origo and c2 is on xaxis Transform2d trans = Transform2d.Translate(Point2d.Origo - ci1.Center) * Transform2d.Rotate(-ci1.Center.Angle(ci2.Center)); ci1 = new Circle2d(ci1); ci1.Transform(trans); ci2 = new Circle2d(ci2); ci2.Transform(trans); li = new Line2d(li); li.Transform(trans); if (!li.ToEquation(out a1, out b1, out c1)) { return(null); //degenerate line } for (int signcase = 0; signcase < 8; ++signcase) { t = ((signcase & 1) == 0) ? 1 : -1; r2 = ((signcase & 2) == 0) ? ci1.Radius : -ci1.Radius; r3 = ((signcase & 4) == 0) ? ci2.Radius : -ci2.Radius; // Get constants a = 2 * (a1 * (r2 - r3) - ci2.X * t); b = 2 * b1 * (r2 - r3); c = 2 * c1 * (r2 - r3) + t * (r2 * r2 - r3 * r3 + ci2.X * ci2.X); if (!MathUtil.IsZero(b)) { u = b1 * c - b * c1; s = a1 * b - a * b1; A = t * t * b * b * (a * a + b * b) - b * b * s * s; B = 2 * (u * t * b * (a * a + b * b) + a * c * s * t * b - b * b * s * s * r2); C = u * u * (a * a + b * b) + 2 * a * c * s * u + c * c * s * s - b * b * s * s * r2 * r2; } else { u = a1 * c - a * c1; s = a * b1; A = a * a * (t * t * a * a - s * s); B = 2 * a * a * (u * t * a - s * s * r2); C = u * u * a * a + c * c * s * s - a * a * s * s * r2 * r2; } // Calculate radius double[] roots = RealPolynomial.SolveQuadric(A, B, C); if (roots != null) { foreach (double radius in roots) { if (radius < minradius || radius > maxradius) { continue; } // compute x coordinates of centers List <double> xsols = new List <double>(); if (!MathUtil.IsZero(ci2.X)) //circles are not concentric { xc = ((r2 + radius) * (r2 + radius) - (r3 + radius) * (r3 + radius) + ci2.X * ci2.X) / (2 * ci2.X); xsols.Add(xc); } else // If circles are concentric there can be 2 solutions for x { A = (a1 * a1 + b1 * b1); B = -2 * a1 * (radius * t - c1); C = (radius * t - c1) * (radius * t - c1) - b1 * b1 * (r2 + radius) * (r2 + radius); double[] roots2 = RealPolynomial.SolveQuadric(A, B, C); if (roots2 != null) { foreach (double x in roots2) { xsols.Add(x); } } } // now compute y coordinates from the calculated x:es // and input the final solution foreach (double x in xsols) { if (!MathUtil.IsZero(b1)) { yc = (-a1 * x - c1 + radius * t) / b1; } else { double ycSquare = (r2 + radius) * (r2 + radius) - (x * x); if (ycSquare < 0.0) { continue; } yc = Math.Sqrt(ycSquare); } AddResult(ref result, x, yc, radius); if (MathUtil.IsZero(b1)) { AddResult(ref result, x, -yc, radius); } } } } } //convert back to original coordinate system by using the inverse //of the original matrix if (result != null) { trans = trans.Inversed; for (int l = 0; l < result.Count; l++) { result[l].Transform(trans); } return(result.ToArray()); } return(null); }
/// <summary> /// Gets the line geometries, one or two lines. The type of the conic has to be /// Intersecting lines (2 results), Paralell lines (2 results) or Coincident lienes (1 result), /// otherwise the result is null. /// </summary> /// <returns></returns> public Line2d[] ToLines() { // Inspired by line extraction conmat.c from book Graphics Gems V double xx, yy; ConicType type = Type; Line2d tmplin; Transform2d tr; GeneralConic2d cpy; List <Line2d> res = null; double de = B * B * 0.25 - A * C; if (MathUtil.IsZero(A) && MathUtil.IsZero(B) && MathUtil.IsZero(C)) { //single line // compute endpoints of the line, avoiding division by zero res = new List <Line2d>(); if (Math.Abs(d) > Math.Abs(e)) { res.Add(new Line2d(-f / (d), 0.0, -(e + f) / (d), 1.0)); } else { res.Add(new Line2d(0.0, -f / (e), 1.0, -(d + f) / (e))); } } else { // two lines cpy = new GeneralConic2d(this); double a = cpy.a, b = cpy.b * 0.5, c = cpy.c, d = cpy.d * 0.5, e = cpy.e * 0.5, f = cpy.f; //get matrix coefficient // use the expression for phi that takes atan of the smallest argument double phi = (Math.Abs(b + b) < Math.Abs(a - c) ? Math.Atan((b + b) / (a - c)) : MathUtil.Deg360 - Math.Atan((a - c) / (b + b))) / 2.0; //phi = cpy.Rotation; if (MathUtil.IsZero(de)) { //parallel lines tr = Transform2d.Rotate(-phi); cpy.Transform(tr); a = cpy.A; b = cpy.B * 0.5; c = cpy.c; d = cpy.d * 0.5; e = cpy.e * 0.5; f = cpy.f; //get matrix coefficient if (Math.Abs(c) < Math.Abs(a)) // vertical { double[] xs = RealPolynomial.SolveQuadric(a, d, f); if (xs != null) { res = new List <Line2d>(); foreach (double x in xs) { tmplin = new Line2d(x, -1, x, 1); tmplin.Transform(tr.Inversed); //back to original spacxe res.Add(tmplin); } } } else //horizontal { double[] ys = RealPolynomial.SolveQuadric(c, e, f, 0.0); if (ys != null) { res = new List <Line2d>(); foreach (double y in ys) { tmplin = new Line2d(-1, y, 1, y); tmplin.Transform(tr.Inversed); res.Add(tmplin); } } } } //end parallel lines case else { //crossing lines Point2d center = Center; double rot = this.Rotation; tr = Transform2d.Translate(-center.X, -center.Y) * Transform2d.Rotate(-rot); cpy.Transform(tr); a = cpy.A; b = cpy.B * 0.5; c = cpy.c; d = cpy.c * 0.5; e = cpy.e * 0.5; f = cpy.f; res = new List <Line2d>(); xx = Math.Sqrt(Math.Abs(1.0 / a)); yy = Math.Sqrt(Math.Abs(1.0 / c)); tr = tr.Inversed; tmplin = new Line2d(-xx, -yy, xx, yy); tmplin.Transform(tr); res.Add(tmplin); tmplin = new Line2d(new Line2d(-xx, yy, xx, -yy)); tmplin.Transform(tr); res.Add(tmplin); } //end crossing lines case } //end two lines if (res == null || res.Count == 0) { return(null); } return(res.ToArray()); }