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 Point2d GetTransformed(Transform2d t) { double tx, ty; t.Apply(X, Y, out tx, out ty, true); return(new Point2d(tx, ty)); }
public void Transform(Transform2d t) { for (int l = 0; l < pts.Count; l++) { pts[l] = pts[l].GetTransformed(t); } }
public Point2d[] Perpendicular(Point2d from) { double i, j; //i,j is from-point in standard space Transform2d tr = ToStandardPosition; tr.Apply(from.X, from.Y, out i, out j, true); //cubic coefficients gotten from langarnge multiplier minimizing distance from i,j to curve double[] xs = RealPolynomial.SolveCubic2(-1, 0.0, j - 0.25, 0.25 * i); if (xs == null) { return(null); } Point2d[] res = new Point2d[xs.Length]; tr = tr.Inversed; int respos = 0; foreach (double x in xs) { tr.Apply(x, x * x, out i, out j, true); res[respos++] = new Point2d(i, j); } return(res); }
public Vector2d GetTransformed(Transform2d t) { double tx, ty; t.Apply(X, Y, out tx, out ty, false); return(new Vector2d(tx, ty)); }
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 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 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 static Point2d[] ParabolaParabola(Parabola2d pab1, Parabola2d pab2) //tested ok { Transform2d tr = pab1.ToStandardPosition; pab2 = new Parabola2d(pab2); //copy for transformation pab2.Transform(tr); var ptset = StandardPosParabolaGeneralConic(pab2.ToGeneralConic()); ptset.Transform(tr.Inversed); return(ptset.ToArray()); }
public bool Contains(Point2d pt) { Transform2d tr = ToStandardPosition; double i, j; tr.Apply(pt.X, pt.Y, out i, out j, true); double dy = i * i - j; //y of parabola in std. pos subtracted with point y return(dy <= 0.0); }
public static Point2d[] HyperbolaHyperbola(Hyperbola2d hyp1, Hyperbola2d hyp2) { Transform2d tr = hyp1.ToStandardPosition; hyp2 = new Hyperbola2d(hyp2); //copy for modification hyp2.Transform(tr); Point2dSet res = StdHyperbolaConic(hyp1.Ratio, hyp2); res.InverseTransform(tr); return(res.ToArray()); }
public override bool Transform(Transform2d t) { //TODO: only manages uniform transformations. Fix this. Vector2d rv = Vector2d.FromAngleAndLength(rotation, FocalDistance); rv = rv.GetTransformed(t); rotation = rv.Angle; vertex = vertex.GetTransformed(t); a = 1.0 / (4 * rv.Length); return(true); }
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)); }
public static Point2d[] ParabolaHyperbola(Parabola2d pab, Hyperbola2d hyp) //tested ok { Transform2d tr = pab.ToStandardPosition; //y=x^2 hyp = new Hyperbola2d(hyp); //copy for transformation hyp.Transform(tr); //to standard space of parabola for stabillity GeneralConic2d gencon = hyp.ToGeneralConic(); var ptset = StandardPosParabolaGeneralConic(gencon); ptset.Transform(tr.Inversed); return(ptset.ToArray()); }
public override bool Transform(Transform2d t) { if (t.IsUniform) { return(false); } start = start.GetTransformed(t); end = end.GetTransformed(t); if (t.Determinant < 0.0) { bulge = -bulge; //mirror } return(true); }
public override bool Transform(Transform2d matrix) { double majax = MajorRadius; double minax = MinorRadius; double rot = Rotation; bool reverse; Ellipse_Transform(ref majax, ref minax, ref rot, ref center, matrix, out reverse); majoraxis = Vector2d.FromAngleAndLength(rot, majax); sigratio = minax / majax; //TODO: clean up this code //TODO: handle reversed ellipse return(true); }
public override bool Transform(Transform2d tr) { //Inspired by conmat.c from graphics gems V, but a lot simplified // Compute M' = Inv(TMat).M.Transpose(Inv(TMat)) Matrix inv = tr.Inversed.ToMatrix(); //Matrix conic = inv.Transposed * ToMatrix() * inv; Matrix conic = inv.Transposed * ToMatrix() * inv; a = conic[0, 0]; // return to conic form b = conic[0, 1] + conic[1, 0]; c = conic[1, 1]; d = conic[0, 2] + conic[2, 0]; e = conic[1, 2] + conic[2, 1]; f = conic[2, 2]; return(true); }
public static Point2d[] HyperbolaEllipse(Hyperbola2d hyp, Ellipse2d elp) { //TODO: this is probably more stable intersecting hyperbola with unitcircle. Rewrite. Transform2d tr = hyp.ToStandardPosition; hyp = new Hyperbola2d(hyp); elp = new Ellipse2d(elp); hyp.Transform(tr); elp.Transform(tr); GeneralConic2d hcon = new GeneralConic2d(1, 0.0, -1 / (hyp.B * hyp.B), 0.0, 0.0, -1); Point2dSet pset = new Point2dSet(); pset.AddRange(ConicConic(hcon, elp.ToGeneralConic())); pset.Transform(tr.Inversed); return(pset.ToArray()); }
public static double[] ParabolaLineParamteric(Parabola2d pab, Line2d lin) //tested ok { double x1, y1, x2, y2; //line points in parabola standard position Transform2d tr = pab.ToStandardPosition; //intersect line with y=x^2 => easier tr.Apply(lin.X1, lin.Y1, out x1, out y1, true); tr.Apply(lin.X2, lin.Y2, out x2, out y2, true); double dx = x2 - x1; double dy = y2 - y1; double c2 = -dx * dx; double c1 = dy - 2 * dx * x1; double c0 = y1 - x1 * x1; //y1-x1^2+t*(dy-2*dx*x1)-dx^2*t^2=0 double[] ts = RealPolynomial.SolveQuadric(c2, c1, c0); return(ts); }
public static Point2d[] EllipseEllipse2(Ellipse2d elp1, Ellipse2d elp2) { //TODO: check if this is better than EllipseEllipse in stabillity and replace it or remove this function Transform2d tr = elp1.ToStandardPosition; elp2 = new Ellipse2d(elp2); //dont alter the original ellipse elp2.Transform(tr); elp1 = new Ellipse2d(elp1); elp1.Transform(tr); GeneralConic2d con1 = new GeneralConic2d(1.0, 0.0, 1 / (elp1.Ratio * elp1.Ratio), 0.0, 0.0, -1.0); GeneralConic2d con2 = elp2.ToGeneralConic(); // GeneralConic2d.FromEllipse(elp2); Point2dSet pset = new Point2dSet(); pset.AddRange(ConicConic(con1, con2)); pset.Transform(tr.Inversed); return(pset.ToArray()); }
/// <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 override bool Transform(Transform2d t) { //transform hyperbola centered at origin, because it's more stable general conic Hyperbola2d hyp = new Hyperbola2d(this); hyp.center = Point2d.Origo; var gencon = hyp.ToGeneralConic(); if (!gencon.Transform(new Transform2d(t.AX, t.AY, t.BX, t.BY, 0.0, 0.0))) { return(false); } hyp = gencon.Reduce() as Hyperbola2d; if (hyp == null) { return(false); } //now transform centerpoint separately, //and write bac to this center = center.GetTransformed(t); ratio = hyp.ratio; majoraxis = hyp.majoraxis; return(true); /* double majax = majoraxis.Length; * double minax = -majax * ratio; * double rot = Rotation; * bool reverse; * Hyperbola_Transform(ref majax, ref minax, ref rot, ref center, t, out reverse); * * majoraxis = Vector2d.FromAngleAndLength(rot, majax); * ratio = minax / majax; * * return true;*/ }
public static double[] HyperbolaLineParametric(Hyperbola2d hyp, Line2d lin) { // Computes intersection points with a hyperbola and a line // solved and created by Robert.P. 2013-02-11 // // solution is created by transforming system to hyperbola standard position, // and then solving the system x^2/a^2-y^2/b^2-1=0 , x=x0+t*dx and y=y0+t*dy (note that a is 1 in std. pos.) Transform2d tr = hyp.ToStandardPosition; //extract standard spaced line double x0, y0, x1, y1, dx, dy, b = hyp.Ratio; tr.Apply(lin.X1, lin.Y1, out x0, out y0, true); tr.Apply(lin.X2, lin.Y2, out x1, out y1, true); dx = x1 - x0; dy = y1 - y0; double t2 = -dy * dy + b * b * dx * dx; double t1 = -2 * dy * y0 + 2 * b * b * dx * x0; double t0 = -y0 * y0 + b * b * x0 * x0 - b * b; return(RealPolynomial.SolveQuadric(t2, t1, t0, 0.0)); }
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 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()); }
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> /// 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()); }
internal static void Ellipse_Transform(ref double a_rh, ref double a_rv, ref double a_offsetrot, ref Point2d endpoint, Transform2d a_mat, out bool a_ReverseWinding) { double rh, rv, rot; double[] m = new double[4]; // matrix representation of transformed ellipse double s, c; // sin and cos helpers (the former offset rotation) double A, B, C; // ellipse implicit equation: double ac, A2, C2; // helpers for angle and halfaxis-extraction. rh = a_rh; rv = a_rv; rot = a_offsetrot; s = Math.Sin(rot); c = Math.Cos(rot); // build ellipse representation matrix (unit circle transformation). // the 2x2 matrix multiplication with the upper 2x2 of a_mat is inlined. m[0] = a_mat.AX * +rh * c + a_mat.BX * rh * s; m[1] = a_mat.AY * +rh * c + a_mat.BY * rh * s; m[2] = a_mat.AX * -rv * s + a_mat.BX * rv * c; m[3] = a_mat.AY * -rv * s + a_mat.BY * rv * c; // to implict equation (centered) A = (m[0] * m[0]) + (m[2] * m[2]); C = (m[1] * m[1]) + (m[3] * m[3]); B = (m[0] * m[1] + m[2] * m[3]) * 2.0f; // precalculate distance A to C ac = A - C; // convert implicit equation to angle and halfaxis: if (MathUtil.IsZero(B)) //=not tilted { if (MathUtil.IsZero(ac) || A > C) { a_offsetrot = 0; } else { a_offsetrot = MathUtil.Deg90; } A2 = A; C2 = C; } else { if (MathUtil.IsZero(ac)) { A2 = A + B * 0.5f; C2 = A - B * 0.5f; a_offsetrot = MathUtil.PI / 4.0f; } else { // Precalculate radical: double K = 1 + B * B / (ac * ac); // Clamp (precision issues might need this.. not likely, but better safe than sorry) if (K < 0) { K = 0; } else { K = Math.Sqrt(K); } A2 = 0.5f * (A + C + K * ac); C2 = 0.5f * (A + C - K * ac); a_offsetrot = 0.5f * Math.Atan2(B, ac); } } // This can get slightly below zero due to rounding issues. // it's save to clamp to zero in this case (this yields a zero length halfaxis) if (A2 < 0) { A2 = 0; } else { A2 = Math.Sqrt(A2); } if (C2 < 0) { C2 = 0; } else { C2 = Math.Sqrt(C2); } // now A2 and C2 are half-axis: if (ac <= 0) { a_rv = A2; a_rh = C2; } else { a_rv = C2; a_rh = A2; } // The transformation matrix might contain a mirror-component, and the // winding order of the ellise needs to be changed. // check the sign of the upper 2x2 submatrix determinant to find out.. a_ReverseWinding = ((a_mat.AX * a_mat.BY) - (a_mat.AY * a_mat.BX)) < 0 ? true : false; // finally, transform ellipse endpoint. This takes care about the // translational part which we ignored at the whole math-showdown above. endpoint = endpoint.GetTransformed(a_mat); }