# 采用Cardinal法构造插枝分段三次样条曲线 : 代码篇

2742人阅读 评论(1)

 版权声明：   本文由timewolf完成，首发于CSDN，作者保留版权。未经许可，不得使用于任何商业用途。欢迎转载，但请保持文章及版权声明完整。如需联络请发邮件：karla9(AT)eyou(dot)com

using System;
using System.Collections;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace CardinalTest
{
/// <summary>
/// the declaration of the Spline, each spline contains 4 control points
/// </summary>
public class Spline
{
// it's the "Pk-1" point
private Point3D m_startControlPoint;
// it's the "Pk" point
private Point3D m_startPoint;
// it's the "Pk+1" point
private Point3D m_endPoint;
// it's the "Pk+2" point
private Point3D m_endControlPoint;
// that means we'll generate 20 sample points between Pk & Pk+1, so "u" will start from 0.00F
// and the increase step is 0.05F
private static readonly int m_sampleCount = 20;
// the "t" in Cardinal altorithm
private static readonly float m_tension = 0.0F;
// store the result sample points
private Point3D[] m_ctrlPoints;
// indicate whether this spline is the first, if it is,
//the m_startControlPoint & m_startPoint will be the same
// because the 4 points can just determine the spline between Pk & Pk+1, so we add a point ahead
// of the Pk-1 manually, so that we can draw the spline between Pk-1 & Pk+1
// similarly, the last Spline's "Pk+2" will be the same as its "Pk+1"
// so that we can draw the spline between Pk+1 & Pk+2
private bool m_isFirst = false;

#region Associated Property Block
public Point3D StartControlPoint
{
get
{
return this.m_startControlPoint;
}
set
{
this.m_startControlPoint = value;
}
}

public Point3D StartPoint
{
get
{
return this.m_startPoint;
}
set
{
this.m_startPoint = value;
}
}

public Point3D EndPoint
{
get
{
return this.m_endPoint;
}
set
{
this.m_endPoint = value;
}
}

public Point3D EndControlPoint
{
get
{
return this.m_endControlPoint;
}
set
{
this.m_endControlPoint = value;
}
}

public Point3D[] CtrlPoints
{
get
{
return this.m_ctrlPoints;
}
}

public bool IsFirst
{
get
{
return this.m_isFirst;
}
set
{
this.m_isFirst = value;
}
}
#endregion

public Spline()
{
m_startControlPoint = new Point3D();
m_startPoint = new Point3D();
m_endPoint = new Point3D();
m_endControlPoint = new Point3D();
m_ctrlPoints = new Point3D[m_sampleCount + 1];
for(int i = 0; i < m_ctrlPoints.Length; i++)
{
m_ctrlPoints[i] = new Point3D();
}
}

/// <summary>
/// A new control point added to the control points, so the previous splines should be updated
/// </summary>
public void AddJoint(Spline prevSpline, Point3D currentPoint)
{
// the previous spline is null, so there's just one point in the control point list;
// so the 4 control points are all the same
// while the 2nd & later points added into the list, the 1st spline's Pk+1 & Pk+2
// will be updated
if (null == prevSpline)
{
this.m_startControlPoint = currentPoint;
this.m_startPoint = currentPoint;
this.m_endPoint = currentPoint;
this.m_endControlPoint = currentPoint;
this.m_isFirst = true;
}
// the previous spline is not null, so update the previous spline's control points
// and update this Spline's points
else
{
// the previous spline is the 1st spline, update its Pk+1 & Pk+2
if (true == prevSpline.m_isFirst)
{
this.m_startControlPoint = prevSpline.StartControlPoint;
this.m_startPoint = prevSpline.StartPoint;
this.m_endPoint = currentPoint;
this.m_endControlPoint = currentPoint;
GenerateSamplePoint();
return;
}
// the previous spline is not the 1st, just update its Pk+2
else
{
prevSpline.EndControlPoint = currentPoint;
prevSpline.GenerateSamplePoint();

// simulate the sample points of current Spline
this.m_startControlPoint = prevSpline.m_startPoint;
this.m_startPoint = prevSpline.m_endPoint;
this.m_endPoint = currentPoint;
this.m_endControlPoint = currentPoint;
GenerateSamplePoint();

}
}
}

/// <summary>
/// use Cardinal altorithm to generate the sample points
/// </summary>
public void GenerateSamplePoint()
{
Point3D startControlPoint = this.StartControlPoint;
Point3D startPoint = this.StartPoint;
Point3D endPoint = this.EndPoint;
Point3D endControlPoint = this.EndControlPoint;
float step = 1.0F / (float)m_sampleCount;
float uValue = 0.00F;

for (int i = 0; i < m_sampleCount; i++)
{
Point3D pointNew = Cardinal(uValue, startControlPoint, startPoint, endPoint, endControlPoint);
this.CtrlPoints[i] = new Point3D(pointNew);
uValue += step;
}
this.CtrlPoints[m_ctrlPoints.Length - 1] = endPoint;
}

public void Draw()
{
for (int i = 0; i < m_ctrlPoints.Length - 1; i++ )
{
Point3D lastP3D = m_ctrlPoints[i];
Point3D nextP3D = m_ctrlPoints[i + 1];
DrawLine(lastP3D, nextP3D, dexStart + i);
}
}

#region Cardinal algorithm
/// <summary>
/// Get a point on the result curve, this point is between startPoint & endPoint
/// </summary>
/// <param name="u">
/// the variable between 0 & 1, the input parameter
/// </param>
/// <param name="startControlPoint">
/// the control point in front of start point, help to determine the appearance of the result curve
/// </param>
/// <param name="startPoint">
/// the start point of the result curve, when u == 0, the returned result is startPoint
/// </param>
/// <param name="endPoint">
/// the end point of the result curve, when u == 1, the returned result is endPoint
/// </param>
/// <param name="endControlPoint">
/// the control point after start point, help to determine the appearance of the result curve
/// </param>
/// <returns>
/// the result point between startPoint & endPoint
/// </returns>
private Point3D Cardinal(float u, Point3D startControlPoint,
Point3D startPoint, Point3D endPoint, Point3D endControlPoint)
{
float s = (1 - m_tension) / 2;
Point3D resultPoint = new Point3D();
resultPoint.X = GetResult(startControlPoint.X, startPoint.X, endPoint.X, endControlPoint.X, s, u);
resultPoint.Y = GetResult(startControlPoint.Y, startPoint.Y, endPoint.Y, endControlPoint.Y, s, u);
resultPoint.Z = GetResult(startControlPoint.Z, startPoint.Z, endPoint.Z, endControlPoint.Z, s, u);
return resultPoint;
}

private float GetResult(float a, float b, float c, float d, float s, float u)
{
float result = 0.0F;
result = a*(2*s*u*u - s*u*u*u - s*u) + b*((2-s)*u*u*u + (s-3)*u*u + 1) +
c*((s-2)*u*u*u + (3-2*s)*u*u + s*u) + d*(s*u*u*u - s*u*u);
return result;
}
#endregion
}
}

namespace CardinalTest
{
/// <summary>
/// Summary description for Point3D.
/// </summary>
public class Point3D
{
private float m_x;
private float m_y;
private float m_z;
public float sizeModulus;

#region Associated Property Declaration
public float X
{
get
{
return m_x;
}
set
{
m_x = value;
}
}

public float Y
{
get
{
return m_y;
}
set
{
m_y = value;
}
}

public float Z
{
get
{
return m_z;
}
set
{
m_z = value;
}
}
#endregion

public Point3D()
{
m_x = 0.0F;
m_y = 0.0F;
m_z = 0.0F;
}

public Point3D(Point3D sourcePoint)
{
m_x = sourcePoint.X;
m_y = sourcePoint.Y;
m_z = sourcePoint.Z;
}

public Point3D(float x, float y, float z)
{
m_x = x;
m_y = y;
m_z = z;
}

public Point3D(double x, double y, double z)
{
m_x = (float)x;
m_y = (float)y;
m_z = (float)z;
}

}
}

/// <summary>
/// Add joint to the path
/// </summary>
/// <param name="nowP3D"></param>
public void AddJoint(Point3D nowP3D)
{
if (null == m_currentSplines || 0 == m_currentSplines.Count)
{
Spline splineNew = new Spline();
}
else
{
Spline splineNew = new Spline();
Spline lastNew = m_currentSplines[m_currentSplines.Count - 1] as Spline;
}
}

/// <summary>
/// Redraw the path and joints
/// </summary>
/// <returns></returns>
public void Draw()
{

int count = m_currentSplines.Count;
for (int i = 0; i < count; i++)
{
Spline spline = m_currentSplines[i] as Spline;
if (true == spline.IsFirst)
{
continue;
}
spline.Draw();
}
}

0
0

* 以上用户言论只代表其个人观点，不代表CSDN网站的观点或立场
个人资料
• 访问：110250次
• 积分：1501
• 等级：
• 排名：千里之外
• 原创：32篇
• 转载：21篇
• 译文：0篇
• 评论：20条
文章分类
阅读排行
最新评论
English
Math