334 lines
7.8 KiB
C#
334 lines
7.8 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using Godot;
|
|
|
|
|
|
namespace Rokojori
|
|
{
|
|
|
|
public class SubdivisionData
|
|
{
|
|
public Vector3 point;
|
|
public bool isCorner;
|
|
public Vector3 direction;
|
|
|
|
public SubdivisionData( Vector3 p, Vector3 d, bool corner = false )
|
|
{
|
|
point = p;
|
|
direction = d;
|
|
isCorner = corner;
|
|
}
|
|
|
|
}
|
|
|
|
public class LerpCurveClosestToPointResult
|
|
{
|
|
public float parameter;
|
|
public float distance;
|
|
public Vector3 point;
|
|
}
|
|
|
|
public class LerpCurve3:Curve3
|
|
{
|
|
List<Vector3> points;
|
|
|
|
public LerpCurve3( List<Vector3> list )
|
|
{
|
|
this.points = list;
|
|
|
|
if ( list.Count == 0 )
|
|
{
|
|
RJLog.Log( "LERP CURVE WITH EMPTY LIST" );
|
|
}
|
|
}
|
|
|
|
public override float ComputeLength( int numSamples )
|
|
{
|
|
var length = 0f;
|
|
var end = points.Count - 1;
|
|
|
|
for ( int i = 0; i < end; i++ )
|
|
{
|
|
var p0 = points[ i ];
|
|
var p1 = points[ i + 1 ];
|
|
|
|
length += p0.DistanceTo( p1 );
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
public int numSegments => points.Count -1;
|
|
public float GetSegmentDistance( int i )
|
|
{
|
|
return ( points[ i + 1 ] - points[ i ] ).Length();
|
|
}
|
|
|
|
public float GetMinimumSegmentDistance()
|
|
{
|
|
var min = GetSegmentDistance( 0 );
|
|
|
|
for ( int i = 1; i < numSegments; i++ )
|
|
{
|
|
min = Mathf.Min( min, GetSegmentDistance( i ) );
|
|
}
|
|
|
|
return min;
|
|
}
|
|
|
|
public LerpCurve3 CreateMinimumSpaced( float minSpace )
|
|
{
|
|
var lastPoint = points.Count - 1;
|
|
|
|
var spacedPoints = new List<Vector3>();
|
|
|
|
var line = new Line3();
|
|
|
|
for ( int i = 0; i < lastPoint; i++ )
|
|
{
|
|
line.start = points[ i ];
|
|
line.end = points[ i + 1 ];
|
|
|
|
var direction = line.direction;
|
|
var length = direction.Length();
|
|
|
|
var numPoints = Mathf.CeilToInt( length / minSpace );
|
|
|
|
for ( int j = 0; j < numPoints - 1; j++ )
|
|
{
|
|
var t = j / (float)( numPoints - 1 );
|
|
spacedPoints.Add( line.PositionAt( t ) );
|
|
}
|
|
}
|
|
|
|
spacedPoints.Add( points[ points.Count - 1 ] );
|
|
|
|
return new LerpCurve3( spacedPoints );
|
|
|
|
}
|
|
|
|
public static LerpCurve3 SampledFrom( Curve3 curve, int numSamples )
|
|
{
|
|
var points = new List<Vector3>();
|
|
|
|
for ( int i = 0; i < numSamples; i++ )
|
|
{
|
|
var t = i / ( (float) numSamples - 1 );
|
|
|
|
var p = curve.PositionAt( t );
|
|
|
|
points.Add( p );
|
|
}
|
|
|
|
return new LerpCurve3( points );
|
|
}
|
|
|
|
public static LerpCurve3 SampledWithResolution( Curve3 curve, float resolution, int lengthSampleResolution = 20 )
|
|
{
|
|
var length = curve.ComputeLength( lengthSampleResolution );
|
|
|
|
var numSamples = Mathf.CeilToInt( length * resolution );
|
|
|
|
return SampledFrom( curve, numSamples );
|
|
}
|
|
|
|
public static LerpCurve3 SampledEqually( Curve3 curve, float minDistance, float smallestSegmentDivision = 2, int lengthSampleResolution = 20 )
|
|
{
|
|
var lerpCurve = SampledWithResolution( curve, minDistance, lengthSampleResolution );
|
|
|
|
var smallestSegment = lerpCurve.GetMinimumSegmentDistance() / smallestSegmentDivision;
|
|
|
|
var minSpace = Mathf.Min( smallestSegment, minDistance );
|
|
|
|
return lerpCurve.CreateMinimumSpaced( minSpace );
|
|
}
|
|
|
|
public float UndistortParameter( float parameter )
|
|
{
|
|
var completeLength = ComputeLength( 0 );
|
|
|
|
var end = points.Count - 1;
|
|
var normalizer = 1f / ( points.Count - 1 );
|
|
|
|
var length = 0f;
|
|
|
|
for ( int i = 0; i < end; i++ )
|
|
{
|
|
var p0 = points[ i ];
|
|
var p1 = points[ i + 1 ];
|
|
var t0 = i * normalizer;
|
|
var t1 = ( i + 1 ) * normalizer;
|
|
var distance = p0.DistanceTo( p1 );
|
|
|
|
if ( t0 <= parameter && parameter <= t1 )
|
|
{
|
|
length += distance * ( parameter - t0 );
|
|
return length / completeLength;
|
|
}
|
|
|
|
length += distance;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
public List<SubdivisionData> Subdivide( float distance, bool close )
|
|
{
|
|
var output = new List<SubdivisionData>();
|
|
|
|
var num = points.Count - 1;
|
|
|
|
if ( close )
|
|
{
|
|
num ++;
|
|
}
|
|
|
|
for ( var i = 0; i < num; i++ )
|
|
{
|
|
var start = points[ i ];
|
|
var end = points[ i == points.Count ? 0 : i + 1 ];
|
|
|
|
var dir = ( end - start );
|
|
var length = dir.Length();
|
|
var samples = Mathf.CeilToInt( length / distance );
|
|
|
|
output.Add( new SubdivisionData( start, dir, true ) );
|
|
|
|
for ( int j = 1; j < samples; j++ )
|
|
{
|
|
var samplePoint = j / (float) samples;
|
|
var point = start.Lerp( end, samplePoint );
|
|
output.Add( new SubdivisionData( point, dir ) );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
if ( ! close )
|
|
{
|
|
var start = points[ points.Count - 2];
|
|
var end = points[ points.Count - 1];
|
|
|
|
var dir = ( end - start );
|
|
|
|
output.Add( new SubdivisionData( end, dir, true ) );
|
|
|
|
}
|
|
|
|
|
|
var dirCount = close ? output.Count : ( output.Count - 1 );
|
|
|
|
for ( int i = 0; i < dirCount; i++ )
|
|
{
|
|
var next = i == output.Count ? 0 : i + 1;
|
|
output[ i ].direction = output[ next ].point - output[ i ].point;
|
|
}
|
|
|
|
|
|
|
|
|
|
return output;
|
|
}
|
|
|
|
public override Vector3 PositionAt( float t )
|
|
{
|
|
if ( t < 0 )
|
|
{
|
|
return points[ 0 ];
|
|
}
|
|
|
|
if ( t >= 1 )
|
|
{
|
|
return points[ points.Count - 1 ];
|
|
}
|
|
|
|
if ( points.Count == 1 )
|
|
{
|
|
return points[ 0 ];
|
|
}
|
|
|
|
|
|
|
|
var listIndex = t * ( points.Count - 1 );
|
|
var flooredIndex = (int) Mathf.Floor( listIndex );
|
|
|
|
var lerpAmount = listIndex - flooredIndex;
|
|
|
|
if ( flooredIndex < 0 || ( flooredIndex >= points.Count - 1 ) )
|
|
{
|
|
RJLog.Log( "Out of range index:",flooredIndex, " t:", t, " listIndex", listIndex, "Count:", points.Count );
|
|
}
|
|
|
|
var lower = points[ flooredIndex ];
|
|
var upper = points[ flooredIndex + 1 ];
|
|
|
|
return Math3D.LerpUnclamped( lower, upper, lerpAmount );
|
|
|
|
}
|
|
|
|
public LerpCurve3 FromRange( Curve3 curve, Range range, int numSamples )
|
|
{
|
|
var points = new List<Vector3>();
|
|
curve.SampleMultiple( range, numSamples, points );
|
|
return new LerpCurve3( points );
|
|
}
|
|
|
|
public LerpCurve3 From( Curve3 curve, int numSamples )
|
|
{
|
|
return FromRange( curve, Range.Of_01, numSamples );
|
|
}
|
|
|
|
public static LerpCurve3 FromPoints( Vector3 first, Vector3 second, params Vector3[] others )
|
|
{
|
|
var points = new List<Vector3>();
|
|
points.Add( first );
|
|
points.Add( second );
|
|
points.AddRange( others );
|
|
|
|
return new LerpCurve3( points );
|
|
}
|
|
|
|
Line3 line;
|
|
|
|
public void GetClosest( Vector3 point, LerpCurveClosestToPointResult result )
|
|
{
|
|
var closestDistance = float.MaxValue;
|
|
Vector3 closestPoint = Vector3.Zero;
|
|
var closestParameter = 0f;
|
|
|
|
if ( line == null )
|
|
{
|
|
line = new Line3( Vector3.Zero, Vector3.Zero );
|
|
}
|
|
|
|
var end = points.Count - 1;
|
|
|
|
var parameterNormalizer = 1f / ( points.Count - 1f );
|
|
|
|
for ( int i = 0; i < end; i++ )
|
|
{
|
|
line.start = points[ i ];
|
|
line.end = points[ i + 1 ];
|
|
|
|
var currentParameter = line.ClostestParameterToPoint( point );
|
|
var currentClosestPoint = line.GetPointAtParameter( currentParameter );
|
|
var currentDistance = ( point - currentClosestPoint ).LengthSquared();
|
|
|
|
if ( currentDistance < closestDistance )
|
|
{
|
|
closestDistance = currentDistance;
|
|
closestPoint = currentClosestPoint;
|
|
closestParameter = ( currentParameter + i ) * parameterNormalizer;
|
|
}
|
|
}
|
|
|
|
result.distance = Mathf.Sqrt( closestDistance );
|
|
result.point = closestPoint;
|
|
result.parameter = closestParameter;
|
|
|
|
}
|
|
|
|
}
|
|
} |