namespace Yw.Curve
{
//贝塞尔曲线类(三次)
public class Bezier3
{
///
///
///
public Bezier3() { }
///
///
///
public Bezier3(List points)
{
if (points == null || points.Count < 4)
{
throw new Exception("参数错误,无法解出三次贝塞尔曲线!");
}
this.Point0 = points[0];
this.Point1 = points[1];
this.Point2 = points[2];
this.Point3 = points[3];
}
///
/// 开始点
///
public CurvePoint Point0 { get; set; }
///
/// 开始点的控制点
///
public CurvePoint Point1 { get; set; }
///
/// 结束点的控制点
///
public CurvePoint Point2 { get; set; }
///
/// 结束点
///
public CurvePoint Point3 { get; set; }
///
/// 获取 X 用于插值
///
public double GetX(double uu)
{
double u0 = Point3.X * uu * uu * uu;
double u1 = 3 * Point2.X * uu * uu * (1 - uu);
double u2 = 3 * Point1.X * uu * (1 - uu) * (1 - uu);
double u3 = Point0.X * (1 - uu) * (1 - uu) * (1 - uu);
return u0 + u1 + u2 + u3;
}
///
/// 获取 Y 用于插值
///
public double GetY(double uu)
{
double u0 = Point3.Y * uu * uu * uu;
double u1 = 3 * Point2.Y * uu * uu * (1 - uu);
double u2 = 3 * Point1.Y * uu * (1 - uu) * (1 - uu);
double u3 = Point0.Y * (1 - uu) * (1 - uu) * (1 - uu);
return u0 + u1 + u2 + u3;
}
///
/// 距离最近的点
///
public CurvePoint GetClosePoint(CurvePoint ref_pt)
{
double boudary_min_x = Math.Min(Point0.X, Point3.X);
double boudary_max_x = Math.Max(Point0.X, Point3.X);
double boudary_min_y = Math.Min(Point0.Y, Point3.Y);
double boudary_max_y = Math.Max(Point0.Y, Point3.Y);
//由于流量扬程数量差别特别大, 所以假设他们在一个100*100的方格内,求距离
double ratio_x = 0;
if (boudary_max_x == boudary_min_x)
{
ratio_x = 100 / (boudary_max_x);
}
else
{
ratio_x = 100 / (boudary_max_x - boudary_min_x);
}
double ratio_y = 0;
if (boudary_max_y == boudary_min_y)
{
ratio_y = 100 / (boudary_max_y);
}
else
{
ratio_y = 100 / (boudary_max_y - boudary_min_y);
}
double chart_ref_x = (ref_pt.X - boudary_min_x) * ratio_x;
double chart_ref_y = (ref_pt.Y - boudary_min_y) * ratio_y;
CurvePoint sect = null;
double min_dist = double.MaxValue;
for (double uu = 0; uu <= 1; uu = uu + 0.01)
{
double chart_x = (GetX(uu) - boudary_min_x) * ratio_x - chart_ref_x;
double chart_y = (GetY(uu) - boudary_min_y) * ratio_y - chart_ref_y;
double distance = chart_x * chart_x + chart_y * chart_y;
if (distance < min_dist)
{
min_dist = distance;
sect = new CurvePoint(GetX(uu), GetY(uu));
}
}
return sect;
}
///
/// 求与曲线的交点 (可能有多个,但只取第一个点)
///
public CurvePoint GetCrossPoint(CurveExpress curve)
{
if (curve == null)
return null;
//第一次迭代
var index1 = GetCrossPointIndex_不迭代(curve, 0.2, 0, 5);
if (index1 < 0)
return null;
//第二次迭代
var index2 = GetCrossPointIndex_不迭代(curve, 0.02, 0.2 * index1, 10);
if (index2 < 0)
return null;
double uu = 0.2 * index1 + 0.02 * index2;
double x_bers = GetX(uu);
double y_bers = FitHelper.GetFitPointY(curve, x_bers);
return new CurvePoint(x_bers, y_bers);
}
//计算在第几个点相交(可能有多个,但只取第一个点):思路, 不断逼近,一个正数一个负数就表示有交点
private int GetCrossPointIndex_不迭代(CurveExpress curve, double space, double start, int number)
{
double last_dis_y = 0;
for (int i = 0; i <= number; i++)
{
double uu1 = start + space * i;
double x_bers = GetX(uu1);
double y_bers = GetY(uu1);
double y_curve = FitHelper.GetFitPointY(curve, x_bers);
double current_dis_y = y_bers - y_curve;
if (uu1 == 0)
{
last_dis_y = current_dis_y;
continue;
}
if ((current_dis_y > 0 && last_dis_y < 0) || (current_dis_y < 0 && last_dis_y > 0))
{
return i - 1;
}
last_dis_y = current_dis_y;
}
return -1;
}
///
/// 变成一条一条直线段
///
/// 贝赛尔曲线中间插入的点数量
public List GetApexPoints(int pointNumber)
{
var points_sect_list = new List();
for (int i = 0; i <= pointNumber; i++)
{
var uu = i * 1.0 / pointNumber;
var p_x = this.GetX(uu);
var p_y = this.GetY(uu);
points_sect_list.Add(new CurvePoint(p_x, p_y));
}
return points_sect_list;
}
///
/// 获取距离
///
public double Distance(CurvePoint point)
{
var point1 = GetClosePoint(point);
return point.Distance(point1);
}
///
/// 通过 Y 判断是否在其中
///
public bool IsInnerByY(double y)
{
if (y >= this.Point0.Y && y <= this.Point3.Y)
return true;
if (y >= this.Point3.Y && y <= this.Point0.Y)
return true;
return false;
}
}
}