/// <summary> /// Intersect a hyperbola in standard position (a=1.0) with a general conic. /// Helper function for other hyperbola intersection functions. /// </summary> /// <param name="hyp1_b"></param> /// <param name="hyp2"></param> private static Point2dSet StdHyperbolaConic(double b1, Conic2d con) { double R = 1.0 / (b1 * b1); Point2dSet res = new Point2dSet(); GeneralConic2d gc = con.ToGeneralConic(); double A = gc.A, B = gc.B, C = gc.C, D = gc.D, E = gc.E, F = gc.F; double y4 = A * A * R * R + (2 * A * C - B * B) * R + C * C; double y3 = (2 * A * E - 2 * B * D) * R + 2 * C * E; double y2 = (-D * D + 2 * A * A + 2 * F * A) * R + E * E + (2 * A + 2 * F) * C - B * B; double y1 = (2 * A + 2 * F) * E - 2 * B * D; double y0 = A * A + 2 * F * A + F * F - D * D; double[] ys = GetRealRoots(y4, y3, y2, y1, y0); if (ys == null) { return(null); } foreach (double y in ys) { //two possible x for each y. Try to find the correct one: double x = Math.Sqrt(y * y * R + 1); double nx = -x; double err = A * x * x + B * x * y + C * y * y + D * x + E * y + F; double nerr = A * nx * nx + B * nx * y + C * y * y + D * nx + E * y + F; if (Math.Abs(err) < Math.Abs(nerr)) { res.Add(new Point2d(x, y)); } else { res.Add(new Point2d(nx, y)); } } return(res); }
/// <summary> /// Intersects a general conic with the curve y=x^2, returning a point set (possibly empty). /// This function is used as a helper function for parabola dominant intersection functions. /// </summary> private static Point2dSet StandardPosParabolaGeneralConic(GeneralConic2d con) //tested ok //c*x^4+b*x^3+(e+a)*x^2+d*x+f { Point2dSet res = new Point2dSet(); double[] xs = GetRealRoots(con.C, con.B, con.E + con.A, con.D, con.F); if (xs == null) { return(res); //no solutions } foreach (double x in xs) { double tx = x; double ty = x * x; res.Add(new Point2d(tx, ty)); } return(res); }
public static Point2d[] ConicConic(GeneralConic2d tcon1, GeneralConic2d tcon2) { // uses the beautiful solution to find the multiplier λ, so that Conic11+λ*Conic2 is // a degenerate conic, that is a conic of lines (the 'pencil'). The lines in this conic // intersects each of the conics in their common intersection points, thus the problem // has been reduced to a Conic-Line intersection problem. // This technique is described in book Graphics Gems V, of which we are inspired although // this code differs a somewhat from the one in the book. // work in standard space for conic 1, gives a more stable computation and speeds up // the multiple line-conic intersections later on. GeneralConic2d con1 = new GeneralConic2d(tcon1); GeneralConic2d con2 = new GeneralConic2d(tcon2); //TODO: does not work properly, probably because line extractor does not work correctly in some cases //convert conic coefficients to their matrix form double a = con1.A, b = con1.B * 0.5, c = con1.C, d = con1.D * 0.5, e = con1.E * 0.5, f = con1.F; double A = con2.A, B = con2.B * 0.5, C = con2.C, D = con2.D * 0.5, E = con2.E * 0.5, F = con2.F; //TODO: since conic 1 is in standard position, thoose can be simplified: b,d,e terms are always zero double c3 = (A * C - B * B) * F - A * E * E + 2 * B * D * E - C * D * D; double c2 = (a * C - 2 * b * B + c * A) * F - a * E * E + (2 * b * D + 2 * d * B - 2 * e * A) * E - c * D * D + (2 * e * B - 2 * d * C) * D + f * A * C - f * B * B; double c1 = (a * c - b * b) * F + (2 * b * d - 2 * a * e) * E + (2 * b * e - 2 * c * d) * D + (a * f - d * d) * C + (2 * d * e - 2 * b * f) * B + (c * f - e * e) * A; double c0 = (a * c - b * b) * f - a * e * e + 2 * b * d * e - c * d * d; double[] lambdas2 = RealPolynomial.SolveCubic2(c3, c2, c1, c0); //up to three coefficients that will turn conic sum to degenerate lines double[] lambdas = GetRealRoots(c3, c2, c1, c0); if (lambdas == null) { return(null); //this can never happen on a 3d degree equation but we check it anyway } Point2dSet res = new Point2dSet(); foreach (double lambda in lambdas) { GeneralConic2d pencil = new GeneralConic2d( a + lambda * A, (b + lambda * B) * 2, c + lambda * C, (d + lambda * D) * 2, (e + lambda * E) * 2, f + lambda * F); Line2d[] lines = pencil.ToLines(); if (lines != null) { foreach (Line2d lin in lines) { //max 2 lines Point2d[] intpts = ConicLine(con1, lin); if (intpts == null) { continue; } //validate each point satisfying the conic equations (they can be out of range for finite conics such as ellipses) foreach (Point2d pt in intpts) { double x = pt.X; double y = pt.Y; double err1 = con1.A * x * x + con1.B * x * y + con1.C * y * y + con1.D * x + con1.E * y + con1.F; if (MathUtil.IsZero(err1, 5.0)) { double err2 = con2.A * x * x + con2.B * x * y + con2.C * y * y + con2.D * x + con2.E * y + con2.F; if (MathUtil.IsZero(err2, 5.0)) { res.Add(pt); } } } } } } //res.Transform(tr.Inversed); return(res.ToArray()); }
public static Point2d[] EllipseCircle(Ellipse2d el, Circle2d ci) { Transform2d tr = el.ToStandardPosition; ci = new Circle2d(ci); //dont modify original circle, but this copy ci.Transform(tr); double b = el.Ratio, b2 = b * b, b4 = b2 * b2; double i = ci.Center.X, i2 = i * i, i4 = i2 * i2; double j = ci.Center.Y, j2 = j * j, j4 = j2 * j2; double r = ci.Radius, r2 = r * r, r4 = r2 * r2; double x4 = b4 - 2 * b2 + 1; double x3 = 4 * b2 * i - 4 * i; double x2 = b2 * (2 * r2 + 2 * j2 - 2 * i2 + 2) - 2 * r2 + 2 * j2 + 6 * i2 - 2 * b4; double x1 = 4 * i * r2 - 4 * i * j2 - 4 * i * i * i - 4 * b2 * i; double x0 = r4 + (-2 * j2 - 2 * i2) * r2 + b2 * (-2 * r2 - 2 * j2 + 2 * i2) + j4 + 2 * i2 * j2 + i4 + b4; //double[] xs = RealPolynomial.SolveQuartic2(x4, x3, x2, x1, x0, 1e-30); RealPolynomial rp = new RealPolynomial(x4, x3, x2, x1, x0); double[] xs = rp.FindRoots(true); if (xs == null) { return(null); //no intersections } Point2dSet resultset = new Point2dSet(); foreach (double x in xs) { //test the two possible y:s to be solutions for this x double y = (1 - x * x) * b2; if (y < 0.0) { continue; } y = Math.Sqrt(y); for (int t = 0; t < 2; t++) //test booth y solutions... { double err = x * x + y * y / b2 - 1.0; //on ellipse double err2 = MathUtil.Square(x - i) + MathUtil.Square(y - j) - r2; //on circle if (MathUtil.IsZero(err, 1e-7) && MathUtil.IsZero(err2, MathUtil.Epsilon)) { resultset.Add(new Point2d(x, y)); } y = -y; // ...by inverting y in second turn } } if (resultset.Count == 0) { return(null); } resultset.Transform(tr.Inversed); //back to original position return(resultset.ToArray()); }
/// <summary> /// Returns all the perpendicular points on the ellipse from a given point 'from' /// </summary> public Point2d[] Perpendicular(Point2d from) { // Solved by Robert.P. in december 2012 // Note on solutions: // Quartic coefficients gotten from applying lagrange multiplier to minimize (x-i)^2+(y-j)^2 // with x^2/a^2+y^2/b^2-1=0 as constraint (a=1 because we work in standard position). // This gives a system of three equations F_x,F_y,F_lambda, which were solved with // resultant theory using 'eliminate' in maxima //work in standard position, retranslate solutions last Transform2d tostd = ToStandardPosition; from = from.GetTransformed(tostd); double b = sigratio, b2 = b * b, b4 = b2 * b2; double i = from.X, i2 = i * i; double j = from.Y, j2 = j * j; double x4 = b4 - 2 * b2 + 1; double x3 = 2 * b2 * i - 2 * i; double x2 = b2 * j2 + i2 - b4 + 2 * b2 - 1; double x1 = 2 * i - 2 * b2 * i; double x0 = -i2; double[] sols = RealPolynomial.SolveQuartic2(x4, x3, x2, x1, x0, 1e-16); if (sols == null) { return(null); } Point2dSet respts = new Point2dSet(); foreach (double x in sols) { double y = (1 - x * x) * b2; if (y < 0.0) { continue; } y = Math.Sqrt(y); for (int l = 0; l < 2; l++) { //both posetive and negative y:s can be solutions. Check with each possible //point that its perpendicular to ellipse (subtracting the inverse ellipse slope (=normal slope) with the slope from 'from' point) double err; err = y * (from.X - x) - x * b2 * (from.Y - y); if (Math.Abs(err) < 1e-6) { respts.Add(new Point2d(x, y)); } y = -y; //test negative solution as well } } respts.Transform(tostd.Inversed); return(respts.ToArray()); }