public static Point2d[] EllipseEllipse(Ellipse2d el1, Ellipse2d el2) { //reduce this problem to a circle-ellipse problem by //rotating ellipse 1 down, scaling it to circle and then //rotate ellipse2 down. Transform2d tr = Transform2d.Rotate(-el1.Rotation) * Transform2d.Stretch(1.0, 1.0 / el1.Ratio); //dont modify originals: el1 = new Ellipse2d(el1); el2 = new Ellipse2d(el2); el1.Transform(tr); el2.Transform(tr); Point2d[] res = EllipseCircle(el2, new Circle2d(el1.X, el1.Y, el1.MajorRadius)); if (res == null) { return(null); } Transform2d trinv = (tr).Inversed; for (int l = 0; l < res.Length; l++) { res[l] = res[l].GetTransformed(trinv); } return(res); }
public override GeneralConic2d ToGeneralConic() { Transform2d tr = Transform2d.Rotate(rotation) * Transform2d.Translate(vertex.X, vertex.Y); GeneralConic2d res = new GeneralConic2d(a, 0, 0, 0, -1, 0); //y=ax^2 res.Transform(tr); return(res); //TODO: optimize this function }
public override GeneralConic2d ToGeneralConic() { Transform2d tr = Transform2d.Scale(MajorRadius) * Transform2d.Rotate(Rotation) * Transform2d.Translate(center.X, center.Y); GeneralConic2d elcon = new GeneralConic2d(1, 0, 1.0 / (sigratio * sigratio), 0, 0, -1); //x^2+(1/b)^2-1=0 => unit ellipse elcon.Transform(tr); //transform conic to position of ellipse return(elcon); //TODO: optimize this function }
public override Point2d PointAt(double t) { double f = FocalDistance; Point2d res = new Point2d(t, a * t * t); //TODO: need to be optimized!! return(res.GetTransformed(Transform2d.Rotate(rotation) * Transform2d.Translate(vertex.X, vertex.Y))); }
public override GeneralConic2d ToGeneralConic() { //TODO: test this function Transform2d tr = Transform2d.Scale(majoraxis.Length) * Transform2d.Rotate(Rotation) * Transform2d.Translate(center.X, center.Y); GeneralConic2d hypcon = new GeneralConic2d(1, 0, -1.0 / (ratio * ratio), 0, 0, -1); //x^2-(y/b)^2-1=0 => unit hypcon.Transform(tr); return(hypcon); //TODO: optimize this function }
public override Vector2d Tangent(Point2d where) { Transform2d tr = Transform2d.Rotate(-rotation); // ToStandardPosition; where = where.GetTransformed(tr); Point2d stdvertex = vertex.GetTransformed(tr); double slope = 2 * a * (where.X - stdvertex.X); ///(4*FocalDistance); //from diffrentiation //TODO: reverse vector if wrong direction return(Vector2d.FromSlope(slope).GetTransformed(tr.Inversed)); }
/// <summary> /// Intersects a line with a conic that is in standard position, that is, not rotated and /// centered at 0,0 /// </summary> /// <param name="con"></param> /// <param name="lin"></param> /// <returns>A list of parameters on line that is intersection points, or null if no intersections</returns> private static double[] ConicLineParametric(GeneralConic2d con, Line2d lin) { //We construct a matrix so that: conic is unrotated (B term=0) and line starts at origo and has length=1.0 //This is to improve stabillity of the equation double invlen = 1.0 / lin.Length; if (double.IsInfinity(invlen)) { return(null); //zero length line does not intersect } Transform2d tr = Transform2d.Translate(-lin.X1, -lin.Y1) * Transform2d.Rotate(-con.Rotation) * Transform2d.Scale(invlen); GeneralConic2d c = new GeneralConic2d(con); //copy for modification double x1 = lin.X2, y1 = lin.Y2; c.Transform(tr); tr.Apply(x1, y1, out x1, out y1, true); //transformed line end double t2 = y1 * y1 * c.C + x1 * x1 * c.A; double t1 = y1 * c.E + x1 * c.D; double t0 = c.F; double[] ts = RealPolynomial.SolveQuadric(t2, t1, t0); return(ts); /*double dx=lin.DX; * double dy=lin.DY; * double x0=lin.X1; * double y0=lin.Y1; * * double t2=con.C*dy*dy+con.A*dx*dx; * double t1=2*con.C*dy*y0+2*con.A*dx*x0+dy*con.E+con.D*dx; * double t0=con.C*y0*y0+con.E*y0+con.A*x0*x0+con.D*x0+con.F; * * return RealPolynomial.SolveQuadric(t2, t1, t0, 1e-9);*/ }
public static Circle2d[] LinePointPoint(Line2d li, Point2d pt1, Point2d pt2) { // max two solutions // solution created by Robert.P. 2013-02-07 List <Circle2d> res = null; // transform problem so that we have a vertical line thru origo which simplifies stuff a lot Transform2d toorg = Transform2d.Translate(-li.X1, -li.Y1) * Transform2d.Rotate(-li.Angle + MathUtil.Deg90); Transform2d fromorg = toorg.Inversed; double i, j, k, l; toorg.Apply(pt1.X, pt1.Y, out i, out j, true); toorg.Apply(pt2.X, pt2.Y, out k, out l, true); double y2 = 2 * k - 2 * i; double y1 = 4 * i * l - 4 * j * k; double y0 = -2 * i * l * l - 2 * i * k * k + (2 * j * j + 2 * i * i) * k; double[] ys = RealPolynomial.SolveQuadric(y2, y1, y0); if (ys == null) { return(null); //no solutions } foreach (double y in ys) { double xx = (y * y - 2 * j * y + j * j + i * i) / (2 * i), yy = y; //TODO: what if vertical line double rad = Math.Abs(xx); fromorg.Apply(xx, yy, out xx, out yy, true); AddResult(ref res, xx, yy, rad); } return(MakeResult(res)); }
public static Circle2d[] CircleCircleCircle(Circle2d ci1, Circle2d ci2, Circle2d ci3) { // see http://www.arcenciel.co.uk/geometry/ for explanation List <Circle2d> result = null; double r1, r2, r3, a, b, c, t, A, B, C; double fRadius, xc, yc, distc1c2; double[] roots; // if all circles concentric, there are no solutions distc1c2 = ci1.Center.Distance(ci2.Center); if (MathUtil.IsZero(distc1c2) && MathUtil.IsZero(ci2.Center.Distance(ci3.Center))) { return(null); } // make sure first 2 circles are not concentric // if so swap ci2,ci3 if (MathUtil.IsZero(distc1c2)) { var tmp = ci2; ci2 = ci3; ci3 = ci2; } // transform input so that ci1 is at origo and ci2 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); ci3 = new Circle2d(ci3); ci3.Transform(trans); // Negate the radii to get all combinations for (int iCase = 0; iCase < 8; ++iCase) { r1 = ((iCase & 1) == 0) ? ci1.Radius : -ci1.Radius; r2 = ((iCase & 2) == 0) ? ci2.Radius : -ci2.Radius; r3 = ((iCase & 4) == 0) ? ci3.Radius : -ci3.Radius; // special case where radii of first 2 circles are equal if (MathUtil.Equals(r1, r2)) { // Calculate x-cordinate of centre xc = ci2.X / 2.0; // if all radii are equal, there will be only one solution if (MathUtil.Equals(r1, r3)) { if (MathUtil.IsZero(ci3.Y)) { continue; } // get y-coordinate of centre yc = (ci3.X * ci3.X - 2.0 * xc * ci3.X + ci3.Y * ci3.Y) / (ci3.Y + ci3.Y); // compute radius A = 1; B = 2 * r1; C = r1 * r1 - xc * xc - yc * yc; roots = RealPolynomial.SolveQuadric(A, B, C); if (roots.Length > 0) { fRadius = roots[0]; if (fRadius <= 0.0) { //then try other root if (roots.Length > 1) { fRadius = roots[1]; if (fRadius <= 0.0) { continue; //no posetive roots } } } AddResult(ref result, xc, yc, fRadius); } } else { // compute constants double k = r1 * r1 - r3 * r3 + ci3.X * ci3.X + ci3.Y * ci3.Y - 2 * xc * ci3.X; A = 4 * ((r1 - r3) * (r1 - r3) - ci3.Y * ci3.Y); B = 4 * (k * (r1 - r3) - 2 * ci3.Y * ci3.Y * r1); C = 4 * xc * xc * ci3.Y * ci3.Y + k * k - 4 * ci3.Y * ci3.Y * r1 * r1; if (!MathUtil.IsZero(A)) { roots = RealPolynomial.SolveQuadric(A, B, C); foreach (double radius in roots) { yc = (2 * radius * (r1 - r3) + k) / (2 * ci3.Y); AddResult(ref result, xc, yc, radius); } } } continue; } //end special case of r1==r2 // Get constants a = 2 * (ci2.X * (r3 - r1) - ci3.X * (r2 - r1)); b = 2 * ci3.Y * (r1 - r2); c = (r2 - r1) * (ci3.X * ci3.X + ci3.Y * ci3.Y - (r3 - r1) * (r3 - r1)) - (r3 - r1) * (ci2.X * ci2.X - (r2 - r1) * (r2 - r1)); t = (ci2.X * ci2.X + r1 * r1 - r2 * r2) / 2.0; A = (r1 - r2) * (r1 - r2) * (a * a + b * b) - (ci2.X * ci2.X * b * b); B = 2 * (t * (r1 - r2) * (a * a + b * b) + a * c * ci2.X * (r1 - r2) - (r1 * ci2.X * ci2.X * b * b)); C = t * t * (a * a + b * b) + (2 * a * c * ci2.X * t) + (c * c * ci2.X * ci2.X) - (r1 * r1 * ci2.X * ci2.X * b * b); // Calculate radius roots = RealPolynomial.SolveQuadric(A, B, C); if (roots == null) { continue; } foreach (double radius in roots) { if (radius < minradius || radius > maxradius) { continue; } // get x coordinate of centre (x2 may not be zero) xc = (radius * (r1 - r2) + t) / ci2.X; // get y coordinate of centre. b should never be 0, as // r1=r2 is special case and y3 may not be zero yc = (-a * xc - c) / b; AddResult(ref result, xc, 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); }
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()); }
public Conic2d Reduce() { //double a = A, b = B, c = C, d = D, e = E, f = F; double na, nb, nc, nd, ne, nf; //coefficients in virtual coordinate system double tilt = 0.0; if (B != 0.0) { //rotated, adjust factors to work in virtual coordinate system tilt = Rotation; double cs = Math.Cos(-tilt); double si = Math.Sin(-tilt); double cs2 = cs * cs; double cssi = cs * si; //create coefficients of unrotated conic (in virtual coordinate system) na = -b * cssi + (a - c) * cs2 + c; nb = -(2 * c - 2 * a) * cssi + 2 * b * cs2 - b; nc = b * cssi + (c - a) * cs2 + a; nd = d * cs - e * si; ne = d * si + e * cs; nf = f; if (!MathUtil.IsZero(nb)) { return(null); //conic is still rotated => irreducable, this should not happen in normal cases } } else //conic is not rotated { na = a; nb = b; nc = c; nd = d; ne = e; nf = f; } double ztol = 1e-9; if (MathUtil.IsZero(na, ztol) && MathUtil.IsZero(nc, ztol)) { //lines => not supported for now. TODO: fix this! return(null); } else if (MathUtil.IsZero(na, ztol)) { //horizontal parabola. if (MathUtil.IsZero(nd, ztol)) { return(null); //TODO: return degenerate conic lines } // compute reduced coefficients double c2 = -nc / nd; double e2 = -ne / nd; double f2 = -nf / nd; // vertex of the parabola Point2d vertex = new Point2d( -(e2 * e2 - 4 * c2 * f2) / (4 * c2), -e2 * .5 / c2).GetTransformed(Transform2d.Rotate(tilt)); // create and return result double focdist = 1.0 / (4.0 * c2); if (focdist < 0.0) { //try to keep posetive focal distance by altering rotation focdist = -focdist; tilt -= MathUtil.Deg180; } return(new Parabola2d(vertex, MathUtil.NormalizeAngle(tilt - MathUtil.Deg90), focdist)); } else if (MathUtil.IsZero(nc, ztol)) { // vertical parabola if (MathUtil.IsZero(ne, ztol)) { return(null); //TODO: return degenerate conic lines } // compute reduced coefficients: double a2 = -na / ne; double d2 = -nd / ne; double f2 = -nf / ne; // vertex of parabola Point2d vertex = new Point2d( //vertex in unrotated sysem of conic... -d2 * 0.5 / a2, -(d2 * d2 - 4 * a2 * f2) / (4 * a2)).GetTransformed(Transform2d.Rotate(tilt)); //...rotated to conic system // create and return result double focdist = 1.0 / (4.0 * a2); if (focdist < 0.0) { //try to keep posetive focal distance focdist = -focdist; tilt -= MathUtil.Deg180; } return(new Parabola2d(vertex, MathUtil.NormalizeAngle(tilt), focdist)); } else { //ellipse or hyperbola // get length of major and minor axis double num = (nc * nd * nd + na * ne * ne - 4 * na * nc * nf) / (4 * na * nc); double sqmajax = num / na; //axes, squared length with sign double sqminax = num / nc; if (MathUtil.IsZero(sqmajax, ztol) || MathUtil.IsZero(sqminax, ztol)) { return(null); //dont allow zero axes } if (sqmajax <= 0.0 && sqminax <= 0.0) { return(null); //both axes cannot be negative } if (sqmajax > 0.0 && sqminax > 0.0) { //ellipse => constructor fixes bt>at by rotating ellipse return(new Ellipse2d(Center, Math.Sqrt(sqmajax), Math.Sqrt(sqminax), tilt)); } else { if (sqmajax > 0.0) //vertical line of symetry => as we define hyperbola { return(new Hyperbola2d(Center, Math.Sqrt(Math.Abs(sqmajax)), Math.Sqrt(Math.Abs(sqminax)), tilt)); } else //horizontal line of symetry => we have to rotate to fit our hyperbola definition { return(new Hyperbola2d(Center, Math.Sqrt(Math.Abs(sqminax)), Math.Sqrt(Math.Abs(sqmajax)), tilt + MathUtil.Deg90)); } } } }