// ReSharper disable once InconsistentNaming public static BezierCurve ResolveXY(PointD point0, PointD point1, PointD point2, PointD point3, PointD vector0, PointD vector3) { var B0 = Polynomial.GetBernstein(0, 3); var B1 = Polynomial.GetBernstein(1, 3); var B2 = Polynomial.GetBernstein(2, 3); var B3 = Polynomial.GetBernstein(3, 3); const double eps = 0.000001D; const double step = 0.001D; // ReSharper disable once InconsistentNaming var uuu_tuu3 = B0 + B1; // ReSharper disable once InconsistentNaming var ttt_ttu3 = B3 + B2; var z1 = ((point1 - point0) * (point2 - point1)).Z; var z2 = ((point2 - point1) * (point3 - point2)).Z; //Точки находятся почти на одной линии if (Math.Abs(z1) < eps && Math.Abs(z2) < eps) { return(new BezierCurve(point0, point0, point3, point3)); } var method1 = new PointD(vector3.Y, -vector3.X); var method2 = new PointD(vector0.Y, -vector0.X); var dot1 = vector0.DotProduct(method1); var dot2 = vector3.DotProduct(method2); var distance = (point0 - point3).Length; //double kMax1 = 10000D * distance / vector0.Length; //double kMax2 = 10000D * distance / vector3.Length; var kMax1 = 1D * distance / vector0.Length; var kMax2 = 1D * distance / vector3.Length; #region Полезные комменты //Идеальный случай //double kMin1 = 0; //double kMin2 = 0; //Классический случай //double kMin1 = 0.001D * distance / vector0.Length; //double kMin2 = 0.001D * distance / vector3.Length; //Point3D s1 = new Point3D(Math.Cos(angle0), Math.Sin(angle0), 0); //Point3D s2 = new Point3D(Math.Cos(angle3), Math.Sin(angle3), 0); //point1 = u1 * u1 * u1 * point0 + 3 * t1 * u1 * u1 * (point0 + k1 * s1) + 3 * t1 * t1 * u1 * (point3 + k2 * s2) + t1 * t1 * t1 * point3; //point2 = u2 * u2 * u2 * point0 + 3 * t2 * u2 * u2 * (point0 + k1 * s1) + 3 * t2 * t2 * u2 * (point3 + k2 * s2) + t2 * t2 * t2 * point3; //3 * t1 * u1 * u1 * k1 * s1 = point1 - u1 * u1 * u1 * point0 - 3 * t1 * u1 * u1 * point0 - 3 * t1 * t1 * u1 * point3 - 3 * t1 * t1 * u1 * k2 * s2 - t1 * t1 * t1 * point3 //3 * t2 * u2 * u2 * k1 * s1 = point2 - u2 * u2 * u2 * point0 - 3 * t2 * u2 * u2 * point0 - 3 * t2 * t2 * u2 * point3 - 3 * t2 * t2 * u2 * k2 * s2 + t2 * t2 * t2 * point3 #endregion //Иначе Corel Draw не распознает слишком близкие числа var kMin1 = 0.01D / vector0.Length; var kMin2 = 0.01D / vector3.Length; var min = double.PositiveInfinity; double k1Min = kMin1, k2Min = -kMin2; var px1 = uuu_tuu3 * point0.X + ttt_ttu3 * point3.X - point2.X; var px2 = B1 * vector0.X; var px3 = B2 * vector3.X; var py1 = uuu_tuu3 * point0.Y + ttt_ttu3 * point3.Y - point2.Y; var py2 = B1 * vector0.Y; var py3 = B2 * vector3.Y; var polyDenomX = point1.X - uuu_tuu3 * point0.X - ttt_ttu3 * point3.X; var polyDenomY = point1.Y - uuu_tuu3 * point0.Y - ttt_ttu3 * point3.Y; for (var t1 = step; t1 < 1D; t1 += step) { #region Находим k1 и k2 для текущего t1 var u1 = 1D - t1; //PointD denominator = point1 - u1 * u1 * u1 * point0 - 3 * t1 * u1 * u1 * point0 - 3 * t1 * t1 * u1 * point3 - t1 * t1 * t1 * point3; var denominator = new PointD(polyDenomX.GetValue(t1), polyDenomY.GetValue(t1)); var k1 = denominator.DotProduct(method1) / (3 * t1 * u1 * u1 * dot1); //double k1 = denominator.DotProduct(method1) / (B1.GetValue(t1) * dot1); if (k1 > kMax1 || k1 < kMin1) { continue; } var k2 = denominator.DotProduct(method2) / (3 * t1 * t1 * u1 * dot2); //double k2 = denominator.DotProduct(method2) / (B0.GetValue(t1) * dot2); if (k2 < -kMax2 || k2 > -kMin2) { continue; } #endregion #region Находим время //Polynomial polynomX = B0 * point0.X + b3 * point3.X - point2.X + B1 * (point0.X + k1 * vector0.X) + B0 * (point3.X + k2 * vector3.X); //Polynomial polynomY = B0 * point0.Y + b3 * point3.Y - point2.Y + B1 * (point0.Y + k1 * vector0.Y) + B0 * (point3.Y + k2 * vector3.Y); var polynomX = px1 + k1 * px2 + k2 * px3; var polynomY = py1 + k1 * py2 + k2 * py3; //Находим корни кубического уравнения var resolve = polynomX.Resolve(false); foreach (var time in resolve) { if (time < 0 || time > 1) { continue; } var value = Math.Abs(polynomY.GetValue(time)); if (value < min) { min = value; k1Min = k1; k2Min = k2; } } #endregion #region Второй метод /* * * for (double t2 = t1 + step; t2 < 1D; t2 += step) * { * double u2 = 1D - t2; * * PointD denominator2 = point2 - u2 * u2 * u2 * point0 - 3 * t2 * u2 * u2 * point0 - 3 * t2 * t2 * u2 * point3 - t2 * t2 * t2 * point3; * double k12 = denominator2.DotProduct(method1) / (3 * t2 * u2 * u2 * dot1); * double k22 = denominator2.DotProduct(method2) / (3 * t2 * t2 * u2 * dot2); * * if (k12 < 0 || k22 > 0) * { * continue; * } * * double d1 = k12 - k1; * double d2 = k22 - k2; * value = d1 * d1 + d2 * d2; * * if (value < min) * { * min = value; * k1min = k1; * k2min = k2; * } * } */ #endregion } //k1min = 0.001D; //k2min = -0.001D; return(new BezierCurve(point0, point0 + k1Min * vector0, point3 + k2Min * vector3, point3)); }