namespace IStation.Curve
|
{
|
/// <summary>
|
/// 贝塞尔曲线类(三次)
|
/// </summary>
|
public class Bezier3
|
{
|
/// <summary>
|
///
|
/// </summary>
|
public Bezier3() { }
|
|
/// <summary>
|
///
|
/// </summary>
|
public Bezier3(List<CurvePoint> 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];
|
}
|
|
/// <summary>
|
/// 开始点
|
/// </summary>
|
public CurvePoint Point0 { get; set; }
|
|
/// <summary>
|
/// 开始点的控制点
|
/// </summary>
|
public CurvePoint Point1 { get; set; }
|
|
/// <summary>
|
/// 结束点的控制点
|
/// </summary>
|
public CurvePoint Point2 { get; set; }
|
|
/// <summary>
|
/// 结束点
|
/// </summary>
|
public CurvePoint Point3 { get; set; }
|
|
/// <summary>
|
/// 获取 X 用于插值
|
/// </summary>
|
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;
|
}
|
|
/// <summary>
|
/// 获取 Y 用于插值
|
/// </summary>
|
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;
|
}
|
|
/// <summary>
|
/// 距离最近的点
|
/// </summary>
|
public CurvePoint GetClosePoint(CurvePoint ref_pt)
|
{
|
double boundary_min_x = Math.Min(Point0.X, Point3.X);
|
double boundary_max_x = Math.Max(Point0.X, Point3.X);
|
double boundary_min_y = Math.Min(Point0.Y, Point3.Y);
|
double boundary_max_y = Math.Max(Point0.Y, Point3.Y);
|
//由于流量扬程数量差别特别大, 所以假设他们在一个100*100的方格内,求距离
|
double ratio_x = 0;
|
if (boundary_max_x == boundary_min_x)
|
{
|
ratio_x = 100 / (boundary_max_x);
|
}
|
else
|
{
|
ratio_x = 100 / (boundary_max_x - boundary_min_x);
|
}
|
|
|
double ratio_y = 0;
|
if (boundary_max_y == boundary_min_y)
|
{
|
ratio_y = 100 / (boundary_max_y);
|
}
|
else
|
{
|
ratio_y = 100 / (boundary_max_y - boundary_min_y);
|
}
|
|
double chart_ref_x = (ref_pt.X - boundary_min_x) * ratio_x;
|
double chart_ref_y = (ref_pt.Y - boundary_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) - boundary_min_x) * ratio_x - chart_ref_x;
|
double chart_y = (GetY(uu) - boundary_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;
|
}
|
|
/// <summary>
|
/// 求与曲线的交点 (可能有多个,但只取第一个点)
|
/// </summary>
|
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;
|
}
|
|
/// <summary>
|
/// 变成一条一条直线段
|
/// </summary>
|
/// <param name="pointNumber">贝赛尔曲线中间插入的点数量</param>
|
public List<CurvePoint> GetApexPoints(int pointNumber)
|
{
|
var points_sect_list = new List<CurvePoint>();
|
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;
|
}
|
|
/// <summary>
|
/// 获取距离
|
/// </summary>
|
public double Distance(CurvePoint point)
|
{
|
var point1 = GetClosePoint(point);
|
return point.Distance(point1);
|
}
|
|
/// <summary>
|
/// 通过 Y 判断是否在其中
|
/// </summary>
|
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;
|
}
|
|
}
|
}
|