public static Intersection CubicBezierLineSegmentIntersection1( double p0x, double p0y, double p1x, double p1y, double p2x, double p2y, double p3x, double p3y, double l0x, double l0y, double l1x, double l1y, double epsilon = Epsilon) { _ = epsilon; // ToDo: Figure out why this can't handle intersection with horizontal lines. var I = new Intersection(IntersectionStates.NoIntersection); var A = l1y - l0y; //A=y2-y1 var B = l0x - l1x; //B=x1-x2 var C = (l0x * (l0y - l1y)) + (l0y * (l1x - l0x)); //C=x1*(y1-y2)+y1*(x2-x1) var xCoeff = CubicBezierBernsteinBasisTests.CubicBezierBernsteinBasis(p0x, p1x, p2x, p3x); var yCoeff = CubicBezierBernsteinBasisTests.CubicBezierBernsteinBasis(p0y, p1y, p2y, p3y); var r = CubicRootsTests.CubicRoots( /* t^3 */ (A * xCoeff.D) + (B * yCoeff.D), /* t^2 */ (A * xCoeff.C) + (B * yCoeff.C), /* t^1 */ (A * xCoeff.B) + (B * yCoeff.B), /* 1 */ (A * xCoeff.A) + (B * yCoeff.A) + C ); /*verify the roots are in bounds of the linear segment*/ for (var i = 0; i < 3; i++) { var t = r[i]; var x = (xCoeff.D * t * t * t) + (xCoeff.C * t * t) + (xCoeff.B * t) + xCoeff.A; var y = (yCoeff.D * t * t * t) + (yCoeff.C * t * t) + (yCoeff.B * t) + yCoeff.A; /*above is intersection point assuming infinitely long line segment, * make sure we are also in bounds of the line*/ double m; m = (l1x - l0x) != 0 ? (x - l0x) / (l1x - l0x) : (y - l0y) / (l1y - l0y); /*in bounds?*/ if (t < 0 || t > 1d || m < 0 || m > 1d) { x = 0; // -100; /*move off screen*/ y = 0; // -100; } else { /*intersection point*/ I.AppendPoint(new Point2D(x, y)); I.State = IntersectionStates.Intersection; } } return(I); }
public static (double X, double Y)? CubicBezierSelfIntersection1(double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3) { var(xCurveA, xCurveB, xCurveC, xCurveD) = CubicBezierBernsteinBasisTests.CubicBezierBernsteinBasis(x0, x1, x2, x3); (var a, var b) = (xCurveD == 0d) ? (xCurveC, xCurveB) : (xCurveC / xCurveD, xCurveB / xCurveD); var(yCurveA, yCurveB, yCurveC, yCurveD) = CubicBezierBernsteinBasisTests.CubicBezierBernsteinBasis(y0, y1, y2, y3); (var p, var q) = (yCurveD == 0d) ? (yCurveC, yCurveB) : (yCurveC / yCurveD, yCurveB / yCurveD); if (a == p || q == b) { return(null); } var k = (q - b) / (a - p); var poly = new double[] { (-k * k * k) - (a * k * k) - (b * k), (3 * k * k) + (2 * k * a) + (2 * b), -3 * k, 2 }; var roots = CubicRootsTests.CubicRoots(poly[3], poly[2], poly[1], poly[0]) .OrderByDescending(c => c).ToArray(); if (roots.Length != 3) { return(null); } if (roots[0] >= 0d && roots[0] <= 1d && roots[2] >= 0d && roots[2] <= 1d) { return(InterpolateCubic2DTests.CubicInterpolate2D(roots[0], x0, y0, x1, y1, x2, y2, x3, y3)); } return(null); }
public static IList <double> QuarticRoots0(double a, double b, double c, double d, double e, double epsilon = Epsilon) { // If a is 0 the polynomial is cubic. if (a is 0d) { return(CubicRootsTests.CubicRoots(b, c, d, e, epsilon)); } var A = b / a; var B = c / a; var C = d / a; var D = e / a; var resolveRoots = CubicRootsTests.CubicRoots( (-A * A * D) + (4d * B * D) - (C * C), (A * C) - (4d * D), -B, 1d, epsilon); var y = resolveRoots[0]; var discriminant = (A * A * OneQuarter) - B + y; // ToDo: May need to switch from a hash set to a list for scan-beams. var results = new HashSet <double>(); if (Abs(discriminant) <= epsilon) { discriminant = 0d; } if (discriminant > 0d) { var ee = Sqrt(discriminant); var t1 = (3d * A * A * OneQuarter) - (ee * ee) - (2d * B); var t2 = ((4d * A * B) - (8d * C) - (A * A * A)) / (4d * ee); var plus = t1 + t2; var minus = t1 - t2; if (Abs(plus) <= epsilon) { plus = 0d; } if (Abs(minus) <= epsilon) { minus = 0d; } if (plus >= 0d) { var f = Sqrt(plus); results.Add((-A * OneQuarter) + ((ee + f) * OneHalf)); results.Add((-A * OneQuarter) + ((ee - f) * OneHalf)); } if (minus >= 0d) { var f = Sqrt(minus); results.Add((-A * OneQuarter) + ((f - ee) * OneHalf)); results.Add((-A * OneQuarter) - ((f + ee) * OneHalf)); } } else if (discriminant < 0d) { } else { var t2 = (y * y) - (4d * D); if (t2 >= -epsilon) { if (t2 < 0) { t2 = 0d; } t2 = 2d * Sqrt(t2); var t1 = (3d * A * A * OneQuarter) - (2d * B); if (t1 + t2 >= epsilon) { var d0 = Sqrt(t1 + t2); results.Add((-A * OneQuarter) + (d0 * OneHalf)); results.Add((-A * OneQuarter) - (d0 * OneHalf)); } if (t1 - t2 >= epsilon) { var d1 = Sqrt(t1 - t2); results.Add((-A * OneQuarter) + (d1 * OneHalf)); results.Add((-A * OneQuarter) - (d1 * OneHalf)); } } } return(results.ToList()); }
public static IList <double> QuarticRootsStephanSmola(double a, double b, double c, double d, double e, double epsilon = Epsilon) { // If a is 0 the polynomial is cubic. if (a is 0d) { return(CubicRootsTests.CubicRoots(b, c, d, e, epsilon)); } var delta = (256d * a * a * a * e * e * e) - (192d * a * a * b * d * e * e) - (128d * a * a * c * c * e * e) + (144d * a * a * c * d * d * e) - (27d * a * a * d * d * d * d) + (144d * a * b * b * c * e * e) - (6d * a * b * b * d * d * e) - (80d * a * b * c * c * d * e) + (18d * a * b * c * d * d * d) + (16d * a * c * c * c * c * e) - (4d * a * c * c * c * d * d) - (27d * b * b * b * b * e * e) + (18d * b * b * b * c * d * e) - (4d * b * b * b * d * d * d) - (4d * b * b * c * c * c * e) + (b * b * c * c * d * d); var P = (8d * a * c) - (3d * b * b); var D = (64d * a * a * a * e) - (16d * a * a * c * c) + (16d * a * b * b * c) - (16d * a * a * b * d) - (3d * b * b * b * b); var d0 = (c * c) - (3d * b * d) + (12d * a * e); var d1 = (2d * c * c * c) - (9d * b * c * d) + (27d * b * b * e) + (27d * a * d * d) - (72d * a * c * e); var p = ((8 * a * c) - (3d * b * b)) / (8d * a * a); var q = ((b * b * b) - (4d * a * b * c) + (8 * a * a * d)) / (8d * a * a * a); var Q = 0d; var S = 0d; var phi = Acos(d1 / (2d * Sqrt(d0 * d0 * d0))); if (double.IsNaN(phi) && (d1 == 0d)) { // if (delta < 0) I guess the new test is ok because we're only interested in real roots Q = d1 + Sqrt((d1 * d1) - (4d * d0 * d0 * d0)); Q /= 2d; Q = Cbrt(Q); S = 0.5d * Sqrt((-2d / 3d * p) + (1d / (3d * a) * (Q + (d0 / Q)))); } else { S = 0.5d * Sqrt((-2d / 3d * p) + (2d / (3d * a) * Sqrt(d0) * Cos(phi / 3d))); } var y = new List <double>(); if (S != 0d) { var R = (-4d * S * S) - (2d * p) + (q / S); if (Abs(R) < epsilon) { R = 0d; } if (R > 0d) { R = 0.5d * Sqrt(R); y.Add((-b / (4 * a)) - S + R); y.Add((-b / (4 * a)) - S - R); } else if (Abs(R) < epsilon) { y.Add((-b / (4d * a)) - S); } R = (-4d * S * S) - (2d * p) - (q / S); if (Abs(R) < epsilon) { R = 0d; } if (R > 0d) { R = 0.5d * Sqrt(R); y.Add((-b / (4d * a)) + S + R); y.Add((-b / (4d * a)) + S - R); } else if (R == 0d) { y.Add((-b / (4d * a)) + S); } } return(y); }
public static Intersection CubicBezierLineIntersection0( double p1X, double p1Y, double p2X, double p2Y, double p3X, double p3Y, double p4X, double p4Y, double a1X, double a1Y, double a2X, double a2Y, double epsilon = Epsilon) { Vector2D a, b, c, d; Vector2D c3, c2, c1, c0; double cl; Vector2D n; var min = MinPointTests.MinPoint(a1X, a1Y, a2X, a2Y); var max = MaxPointTests.MaxPoint(a1X, a1Y, a2X, a2Y); var result = new Intersection(IntersectionStates.NoIntersection); a = new Vector2D(p1X, p1Y) * (-1); b = new Vector2D(p2X, p2Y) * 3; c = new Vector2D(p3X, p3Y) * (-3); d = a + (b + (c + new Vector2D(p4X, p4Y))); c3 = new Vector2D(d.I, d.J); a = new Vector2D(p1X, p1Y) * 3; b = new Vector2D(p2X, p2Y) * (-6); c = new Vector2D(p3X, p3Y) * 3; d = a + (b + c); c2 = new Vector2D(d.I, d.J); a = new Vector2D(p1X, p1Y) * (-3); b = new Vector2D(p2X, p2Y) * 3; c = a + b; c1 = new Vector2D(c.I, c.J); c0 = new Vector2D(p1X, p1Y); n = new Vector2D(a1Y - a2Y, a2X - a1X); cl = (a1X * a2Y) - (a2X * a1Y); var roots = CubicRootsTests.CubicRoots( DotProduct2Vector2DTests.DotProduct2D(n.I, n.J, c3.I, c3.J), DotProduct2Vector2DTests.DotProduct2D(n.I, n.J, c2.I, c2.J), DotProduct2Vector2DTests.DotProduct2D(n.I, n.J, c1.I, c1.J), DotProduct2Vector2DTests.DotProduct2D(n.I, n.J, c0.I + cl, c0.J + cl), epsilon); for (var i = 0; i < roots.Count; i++) { var t = roots[i]; if (0 <= t && t <= 1) { var p5 = InterpolateLinear2DTests.LinearInterpolate2D(t, p1X, p1Y, p2X, p2Y); var p6 = InterpolateLinear2DTests.LinearInterpolate2D(t, p2X, p2Y, p3X, p3Y); var p7 = InterpolateLinear2DTests.LinearInterpolate2D(t, p3X, p3Y, p4X, p4Y); var p8 = InterpolateLinear2DTests.LinearInterpolate2D(t, p5.X, p5.Y, p6.X, p6.Y); var p9 = InterpolateLinear2DTests.LinearInterpolate2D(t, p6.X, p6.Y, p7.X, p7.Y); var p10 = InterpolateLinear2DTests.LinearInterpolate2D(t, p8.X, p8.Y, p9.X, p9.Y); if (a1X == a2X) { result.State = IntersectionStates.Intersection; result.AppendPoint(p10); } else if (a1Y == a2Y) { result.State = IntersectionStates.Intersection; result.AppendPoint(p10); } else if (GreaterThanOrEqualTests.GreaterThanOrEqual(p10.X, p10.Y, min.X, min.Y) && LessThanOrEqualTests.LessThanOrEqual(p10.X, p10.Y, max.X, max.Y)) { result.State = IntersectionStates.Intersection; result.AppendPoint(p10); } } } return(result); }
public static Intersection QuadraticBezierSegmentQuadraticBezierSegmentIntersection1( double a1X, double a1Y, double a2X, double a2Y, double a3X, double a3Y, double b1X, double b1Y, double b2X, double b2Y, double b3X, double b3Y, double epsilon = Epsilon) { var result = new Intersection(IntersectionStates.NoIntersection); // ToDo: Break early if the AABB bounding box of the curve does not intersect. // ToDo: Figure out if the following can be broken out of the vector structs. var c12 = new Vector2D(a1X - (a2X * 2) + a3X, a1Y - (a2Y * 2) + a3Y); var c11 = new Vector2D(2 * (a2X - a1X), 2 * (a2Y - a1Y)); // c10 is a1X and a1Y var c22 = new Vector2D(b1X - (b2X * 2) + b3X, b1Y - (b2Y * 2) + b3Y); var c21 = new Vector2D(2 * (b2X - b1X), 2 * (b2Y - b1Y)); // c20 is b1X and b1Y var a = (c12.I * c11.J) - (c11.I * c12.J); var b = (c22.I * c11.J) - (c11.I * c22.J); var c = (c21.I * c11.J) - (c11.I * c21.J); var d = (c11.I * (a1Y - b1Y)) - (c11.J * (b1X - a1X)); var e = (-c22.I * c12.J) - (c12.I * c22.J); var f = (c21.I * c12.J) - (c12.I * c21.J); var g = (c12.I * (a1Y - b1Y)) - (c12.J * (b1X - a1X)); IList <double> roots; if ((a * d) - (g * g) == 0) { var v0 = (a * c) - (2 * f * g); var v1 = (a * b) - (f * f) - (2 * e * g); var v2 = -2 * e * f; var v3 = -e * e; roots = CubicRootsTests.CubicRoots( /* t^3 */ -v3, /* t^2 */ -v2, /* t^1 */ -v1, /* C */ -v0, epsilon); } else { var v0 = (a * d) - (g * g); var v1 = (a * c) - (2 * f * g); var v2 = (a * b) - (f * f) - (2 * e * g); var v3 = -2 * e * f; var v4 = -e * e; roots = QuarticRootsTests.QuarticRoots( /* t^4 */ -v4, /* t^3 */ -v3, /* t^2 */ -v2, /* t^1 */ -v1, /* C */ -v0, epsilon); } //roots.Reverse(); foreach (var s in roots) { var point = new Point2D( (c22.I * s * s) + (c21.I * s) + b1X, (c22.J * s * s) + (c21.J * s) + b1Y); if (s >= 0 && s <= 1) { var v0 = a1X - point.X; var v1 = c11.I; var v2 = c12.I; var xRoots = QuadraticRootsTests.QuadraticRoots( /* t^2 */ -v2, /* t^1 */ -v1, /* C */ -v0, epsilon); v0 = a1Y - point.Y; v1 = c11.J; v2 = c12.J; var yRoots = QuadraticRootsTests.QuadraticRoots( /* t^2 */ -v2, /* t^1 */ -v1, /* C */ -v0, epsilon); if (xRoots.Count > 0 && yRoots.Count > 0) { // Find the nearest matching x and y roots in the ranges 0 < x < 1; 0 < y < 1. foreach (var xRoot in xRoots) { if (xRoot >= 0 && xRoot <= 1) { foreach (var yRoot in yRoots) { var t = xRoot - yRoot; if ((t >= 0 ? t : -t) < 0.1) { result.AppendPoint(point); goto checkRoots; // Break through two levels of foreach loops. Using goto for performance. } } } } checkRoots :; } } } if (result.Items.Count > 0) { result.State = IntersectionStates.Intersection; } return(result); }