예제 #1
0
        // 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));
        }