■サンプル
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Linq;
public class BSpline
{
public enum KnotType
{
Uniform,
OpenUniform,
}
private List<Vector3> m_ctrlPt;
private List<float> m_knot;
private KnotType m_knotType;
private int m_degree;
/// <summary>
/// B-Splineを生成する
/// </summary>
/// <param name="ctrlPt">制御点</param>
/// <param name="degree">次数</param>
/// <param name="knotType">Knotの種類(Uniform or OpenUniform</param>
public BSpline(Vector3[] ctrlPt, int degree, KnotType knotType)
{
m_ctrlPt = new List<Vector3>();
m_ctrlPt.AddRange(ctrlPt);
switch (knotType)
{
case KnotType.Uniform:
m_knot = createUniformKnotVector(ctrlPt.Count(), degree);
break;
case KnotType.OpenUniform:
m_knot = createOpenUniformKnotVector(ctrlPt.Count(), degree);
break;
default:
Debug.Assert(false);
throw new ArgumentException();
}
m_knotType = knotType;
m_degree = degree;
}
/// <summary>
/// B-Splineを生成する
/// </summary>
/// <param name="resolution">生成するの数</param>
/// <returns></returns>
public List<Vector3> evaluate(int resolution)
{
List<Vector3> result = new List<Vector3>();
if (m_degree == 1 || resolution < m_ctrlPt.Count || m_ctrlPt.Count < 2)
{
result.AddRange(m_ctrlPt);
}
else
{
float minKnot = m_knot[m_degree];
float maxKnot = m_knot[m_knot.Count - m_degree - 1];
float t = minKnot;
float step = (maxKnot - minKnot) / (resolution - 1.0f);
while (true)
{
result.Add(calc(t));
if (t == maxKnot)
break;
t += step;
if (t > maxKnot)
t = maxKnot;
}
if (m_knotType == KnotType.OpenUniform)
{
result[result.Count - 1] = m_ctrlPt.Last();
}
}
return result;
}
/// <summary>
/// t点の位置を求める
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
private Vector3 calc(float t)
{
Vector3 pt = new Vector3();
int count = m_ctrlPt.Count;
for (int i = 0; i < count; i++)
{
float w = deboor(i, m_degree, t);
if (w > 0.0f)
pt += m_ctrlPt[i] * w;
}
return pt;
}
// de boor cox
private float deboor(int i, int degree, float t)
{
if (degree == 0)
{
return (m_knot[i] <= t && t < m_knot[i + 1]) ? 1 : 0;
}
float w1 = 0.0f, w2 = 0.0f;
if (m_knot[i + degree] - m_knot[i] != 0)
w1 = ((t - m_knot[i]) / (m_knot[i + degree] - m_knot[i]));
if (m_knot[i + degree + 1] - m_knot[i + 1] != 0)
w2 = ((m_knot[i + degree + 1] - t) / (m_knot[i + degree + 1] - m_knot[i + 1]));
if (w1 != 0)
w1 *= deboor(i, degree - 1, t);
if (w2 != 0)
w2 *= deboor(i + 1, degree - 1, t);
return w1 + w2;
}
/// <summary>
/// 一様ノットベクトルの作成
/// </summary>
/// <param name="controlPointCount"></param>
/// <param name="degree"></param>
/// <returns></returns>
private List<float> createUniformKnotVector(int controlPointCount, int degree)
{
List<float> knot = new List<float>();
int knotCount = controlPointCount + degree + 1;
float t = 0;
for (int i = 0; i < knotCount; i++)
{
knot.Add((1.0f / (knotCount - 1)) * i);
}
return knot;
}
/// <summary>
/// 開一様ノットベクトルの作成
/// </summary>
/// <param name="controlPoint"></param>
/// <param name="degree"></param>
/// <returns></returns>
private List<float> createOpenUniformKnotVector(int controlPointCount, int degree)
{
List<float> knot = new List<float>();
int knotCount = controlPointCount + degree + 1;
float step = knotCount - ((degree + 1) * 2);
step = 1.0f / (step + 1.0f);
for (int i = 0; i < knotCount; i++)
{
// 最初のn+1のノットは0
if (i < degree + 1)
knot.Add(0);
// 最後のn+1のノットは1
else if (i >= knotCount - (degree + 1))
knot.Add(1);
// それ以外は補完する
else
knot.Add(step * (i - degree));
}
return knot;
}
}
■補足
次数が固定なら予め漸化式を計算しておいた方が良い。