341 lines
7.6 KiB
C#
341 lines
7.6 KiB
C#
using System;
|
|
using UnityEngine;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
|
|
/**
|
|
* Obi spline class.
|
|
*/
|
|
|
|
namespace Obi{
|
|
|
|
|
|
/**
|
|
* Bèzier spline. Provides tangent handles at the control points, which offers greater control compared to
|
|
* Catmull-rom splines.
|
|
*/
|
|
[ExecuteInEditMode]
|
|
public class ObiBezierCurve : ObiCurve {
|
|
|
|
|
|
public enum BezierCPMode {
|
|
Free,
|
|
Aligned,
|
|
Mirrored
|
|
}
|
|
|
|
[HideInInspector] public List<BezierCPMode> controlPointModes = null;
|
|
[HideInInspector] public BezierCPMode lastOpenCPMode;
|
|
[HideInInspector] public Vector3 lastOpenCP;
|
|
|
|
public override void Awake(){
|
|
minPoints = 4;
|
|
unusedPoints = 2;
|
|
pointStride = 3;
|
|
base.Awake();
|
|
if (controlPointModes == null){
|
|
controlPointModes = new List<BezierCPMode>(){BezierCPMode.Free,BezierCPMode.Free};
|
|
}
|
|
}
|
|
|
|
protected override void SetClosed(bool closed){
|
|
|
|
if (this.closed == closed) return;
|
|
|
|
if (!this.closed && closed){
|
|
lastOpenCP = controlPoints[0];
|
|
lastOpenCPMode = controlPointModes[0];
|
|
controlPoints[0] = controlPoints[controlPoints.Count-1];
|
|
controlPointModes[0] = controlPointModes[controlPointModes.Count-1];
|
|
}else{
|
|
controlPoints[0] = lastOpenCP;
|
|
controlPointModes[0] = lastOpenCPMode;
|
|
}
|
|
|
|
this.closed = closed;
|
|
EnforceMode(0);
|
|
}
|
|
|
|
public override int GetNumSpans(){
|
|
|
|
return ( controlPoints.Count + ((controlPoints.Count-4)/3) ) / 4;
|
|
|
|
}
|
|
|
|
public bool IsHandle(int index){
|
|
return index % 3 != 0;
|
|
}
|
|
|
|
public int GetHandleControlPointIndex(int index){
|
|
|
|
if (index < 0 || index >= controlPoints.Count)
|
|
return -1;
|
|
|
|
if (index % 3 == 1)
|
|
return index-1;
|
|
else if (index % 3 == 2)
|
|
return index+1;
|
|
else return index;
|
|
|
|
}
|
|
|
|
public List<int> GetHandleIndicesForControlPoint(int index){
|
|
|
|
List<int> handleIndices = new List<int>();
|
|
if (index < 0 || index >= controlPoints.Count) return handleIndices;
|
|
|
|
if (!IsHandle(index)) {
|
|
|
|
if (closed) {
|
|
if (index == 0) {
|
|
handleIndices.Add(1);
|
|
handleIndices.Add(controlPoints.Count - 2);
|
|
}
|
|
else if (index == controlPoints.Count - 1) {
|
|
handleIndices.Add(1);
|
|
handleIndices.Add(index - 1);
|
|
}
|
|
else {
|
|
handleIndices.Add(index + 1);
|
|
handleIndices.Add(index - 1);
|
|
}
|
|
}
|
|
else {
|
|
if (index > 0) {
|
|
handleIndices.Add(index - 1);
|
|
}
|
|
if (index + 1 < controlPoints.Count) {
|
|
handleIndices.Add(index + 1);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return handleIndices;
|
|
|
|
}
|
|
|
|
public override void DisplaceControlPoint(int index, Vector3 delta){
|
|
|
|
if (index < 0 || index >= controlPoints.Count) return;
|
|
|
|
if (!IsHandle(index)) {
|
|
|
|
if (closed) {
|
|
if (index == 0) {
|
|
controlPoints[1] += delta;
|
|
controlPoints[controlPoints.Count - 2] += delta;
|
|
controlPoints[controlPoints.Count - 1] += delta;
|
|
}
|
|
else if (index == controlPoints.Count - 1) {
|
|
controlPoints[0] += delta;
|
|
controlPoints[1] += delta;
|
|
controlPoints[index - 1] += delta;
|
|
}
|
|
else {
|
|
controlPoints[index - 1] += delta;
|
|
controlPoints[index + 1] += delta;
|
|
}
|
|
}
|
|
else {
|
|
if (index > 0) {
|
|
controlPoints[index - 1] += delta;
|
|
}
|
|
if (index + 1 < controlPoints.Count) {
|
|
controlPoints[index + 1] += delta;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
controlPoints[index] += delta;
|
|
EnforceMode(index);
|
|
|
|
}
|
|
|
|
public override int GetSpanControlPointForMu(float mu, out float spanMu){
|
|
|
|
int spanCount = GetNumSpans();
|
|
spanMu = mu * spanCount;
|
|
int i = (mu >= 1f) ? (spanCount - 1) : (int) spanMu;
|
|
spanMu -= i;
|
|
|
|
return i * 3;
|
|
}
|
|
|
|
public BezierCPMode GetControlPointMode (int index) {
|
|
int i = (index + 1) / 3;
|
|
return controlPointModes[i];
|
|
}
|
|
|
|
public void SetControlPointMode (int index, BezierCPMode mode) {
|
|
int i = (index + 1) / 3;
|
|
controlPointModes[i] = mode;
|
|
|
|
if (closed) {
|
|
if (i == 0) {
|
|
controlPointModes[controlPointModes.Count - 1] = mode;
|
|
}
|
|
else if (i == controlPointModes.Count - 1) {
|
|
controlPointModes[0] = mode;
|
|
}
|
|
}
|
|
|
|
EnforceMode(index);
|
|
}
|
|
|
|
public void EnforceMode (int index) {
|
|
int modeIndex = (index + 1) / 3;
|
|
BezierCPMode mode = controlPointModes[modeIndex];
|
|
if (mode == BezierCPMode.Free || !closed && (modeIndex == 0 || modeIndex == controlPointModes.Count - 1)) {
|
|
return;
|
|
}
|
|
|
|
int middleIndex = modeIndex * 3;
|
|
int fixedIndex, enforcedIndex;
|
|
if (index <= middleIndex) {
|
|
fixedIndex = middleIndex - 1;
|
|
if (fixedIndex < 0) {
|
|
fixedIndex = controlPoints.Count - 2;
|
|
}
|
|
enforcedIndex = middleIndex + 1;
|
|
if (enforcedIndex >= controlPoints.Count) {
|
|
enforcedIndex = 1;
|
|
}
|
|
}
|
|
else {
|
|
fixedIndex = middleIndex + 1;
|
|
if (fixedIndex >= controlPoints.Count) {
|
|
fixedIndex = 1;
|
|
}
|
|
enforcedIndex = middleIndex - 1;
|
|
if (enforcedIndex < 0) {
|
|
enforcedIndex = controlPoints.Count - 2;
|
|
}
|
|
}
|
|
|
|
Vector3 middle = controlPoints[middleIndex];
|
|
Vector3 enforcedTangent = middle - controlPoints[fixedIndex];
|
|
if (mode == BezierCPMode.Aligned) {
|
|
enforcedTangent = enforcedTangent.normalized * Vector3.Distance(middle, controlPoints[enforcedIndex]);
|
|
}
|
|
controlPoints[enforcedIndex] = middle + enforcedTangent;
|
|
}
|
|
|
|
public void AddSpan(){
|
|
|
|
int index = controlPoints.Count-1;
|
|
|
|
Vector3 lastPosition = controlPoints[index];
|
|
|
|
controlPoints.Add(lastPosition+Vector3.right*0.5f);
|
|
controlPoints.Add(lastPosition+Vector3.right);
|
|
controlPoints.Add(lastPosition+Vector3.right*1.5f);
|
|
|
|
controlPointModes.Add(ObiBezierCurve.BezierCPMode.Free);
|
|
|
|
EnforceMode(index);
|
|
|
|
if (closed) {
|
|
controlPoints[controlPoints.Count - 1] = controlPoints[0];
|
|
controlPointModes[controlPointModes.Count - 1] = controlPointModes[0];
|
|
EnforceMode(0);
|
|
}
|
|
|
|
}
|
|
|
|
public void RemoveCurvePoint(int curvePoint){
|
|
|
|
if (controlPoints.Count <= 4) return;
|
|
|
|
int firstPoint = Mathf.Max(0,curvePoint * 3 - 1);
|
|
int numPoints = 3;
|
|
|
|
// First and last spans have 1 point less to remove.
|
|
if (firstPoint == controlPoints.Count-2)
|
|
firstPoint -= 1;
|
|
|
|
controlPoints.RemoveRange(firstPoint,numPoints);
|
|
controlPointModes.RemoveAt(curvePoint);
|
|
|
|
if (closed){
|
|
if(firstPoint == controlPoints.Count){
|
|
controlPoints[0] = controlPoints[controlPoints.Count-1];
|
|
controlPointModes[0] = controlPointModes[controlPointModes.Count-1];
|
|
}else if (firstPoint == 0){
|
|
controlPoints[controlPoints.Count-1] = controlPoints[0];
|
|
controlPointModes[controlPointModes.Count-1] = controlPointModes[0];
|
|
}
|
|
}
|
|
|
|
EnforceMode(firstPoint);
|
|
|
|
}
|
|
|
|
/**
|
|
* 1D bezier spline interpolation
|
|
*/
|
|
protected override float Evaluate1D(float y0, float y1, float y2, float y3, float mu){
|
|
|
|
float imu = 1 - mu;
|
|
return imu * imu * imu * y0 +
|
|
3f * imu * imu * mu * y1 +
|
|
3f * imu * mu * mu * y2 +
|
|
mu * mu * mu * y3;
|
|
|
|
/*float a0,a1,a2,a3,mu2;
|
|
mu2 = mu*mu;
|
|
|
|
a0 = -0.5f*y0 + 1.5f*y1 - 1.5f*y2 + 0.5f*y3;
|
|
a1 = y0 - 2.5f*y1 + 2f*y2 - 0.5f*y3;
|
|
a2 = -0.5f*y0 + 0.5f*y2;
|
|
a3 = y1;
|
|
|
|
return(a0*mu*mu2+a1*mu2+a2*mu+a3);*/
|
|
|
|
}
|
|
|
|
/**
|
|
* 1D catmull rom spline second derivative
|
|
*/
|
|
protected override float EvaluateFirstDerivative1D(float y0, float y1, float y2, float y3, float mu){
|
|
|
|
float imu = 1 - mu;
|
|
return 3f * imu * imu * (y1 - y0) +
|
|
6f * imu * mu * (y2 - y1) +
|
|
3f * mu * mu * (y3 - y2);
|
|
|
|
/*float a0,a1,a2,mu2;
|
|
mu2 = mu*mu;
|
|
|
|
a0 = -0.5f*y0 + 1.5f*y1 - 1.5f*y2 + 0.5f*y3;
|
|
a1 = y0 - 2.5f*y1 + 2f*y2 - 0.5f*y3;
|
|
a2 = -0.5f*y0 + 0.5f*y2;
|
|
|
|
return(3*a0*mu2 + 2*a1*mu + a2);*/
|
|
}
|
|
|
|
|
|
/**
|
|
* 1D catmull rom spline second derivative
|
|
*/
|
|
protected override float EvaluateSecondDerivative1D(float y0, float y1, float y2, float y3, float mu){
|
|
|
|
float imu = 1 - mu;
|
|
return 3f * imu * imu * (y1 - y0) +
|
|
6f * imu * mu * (y2 - y1) +
|
|
3f * mu * mu * (y3 - y2);
|
|
|
|
/*float a0,a1,a2;
|
|
|
|
a0 = -0.5f*y0 + 1.5f*y1 - 1.5f*y2 + 0.5f*y3;
|
|
a1 = y0 - 2.5f*y1 + 2f*y2 - 0.5f*y3;
|
|
|
|
return(6*a0*mu + 2*a1 );*/
|
|
|
|
}
|
|
|
|
|
|
}
|
|
}
|