rj-action-library/Runtime/Math/Geometry/Curve2.cs

199 lines
5.2 KiB
C#

using Godot;
using System.Collections;
using System.Collections.Generic;
using System;
namespace Rokojori
{
public abstract class Curve2
{
float _gradientSamplingRange = Mathf.Pow( 10f, -8f );
public abstract Vector2 SampleAt( float t );
public virtual void SampleMultiple( int numSamples, List<Vector2> output )
{
SampleMultiple( new Range( 0, 1 ), numSamples, output );
}
public virtual void SampleMultiple( Range range, int numSamples, List<Vector2> output )
{
var diff = range.length / ( numSamples - 1 ) ;
for ( var i = 0 ; i < numSamples; i++ )
{
var t = range.min + i * diff;
output.Add( SampleAt( t ) );
}
}
public virtual Path2 SamplePath( int numSamples )
{
return SamplePath( new Range( 0, 1 ), numSamples );
}
public virtual Path2 SamplePath( Range range, int numSamples )
{
var diff = range.length / ( numSamples - 1 ) ;
var points = new List<Vector2>();
for ( var i = 0 ; i < numSamples; i++ )
{
var t = range.min + i * diff;
var p = SampleAt( t );
points.Add( new Vector2( p.X, p.Y ) );
}
return new Path2( points );
}
public virtual Vector2 GradientAt( float t )
{
return GradientAt( t, _gradientSamplingRange );
}
public virtual Vector2 GradientAt( float t, float samplingRange )
{
return SampleAt( t + samplingRange ) - SampleAt( t - samplingRange );
}
public virtual float ComputeLength( int numSamples )
{
return ComputeSubLength( Range.Of_01 , numSamples );
}
public virtual float ComputeSubLength( Range range, int numSamples )
{
var diff = range.length / ( numSamples - 1 ) ;
var it = SampleAt( range.min );
var length = 0f;
for ( var i = 1 ; i < numSamples; i++ )
{
var t = range.min + i * diff;
var next = SampleAt( t );
length += ( next - it ).Length();
it = next;
}
return length;
}
public virtual int ComputeNumSamplesFor( Range range,
float minimumDistance, int minSamples = 2,
int numSamplesForLengthComputation = -1 )
{
if ( minSamples < 2 )
{ minSamples = 2; }
if ( numSamplesForLengthComputation <= 0 )
{ numSamplesForLengthComputation = minSamples * 4; }
float distance = ComputeSubLength( range, numSamplesForLengthComputation );
float numFloatingCuts = distance / minimumDistance;
int numSamples = Mathf.CeilToInt( numFloatingCuts );
return Mathf.Max( minSamples, numSamples );
}
public Vector2 GetClosestPositionTo( Vector2 position, int numSegments = 20, int depth = 0 )
{
var parameter = GetClosestParameterTo( position, numSegments, depth );
return SampleAt( parameter );
}
public float GetClosestParameterTo( Vector2 point, int numSegments = 20, int depth = 3 )
{
return GetClosestParameterTo( 0, 1, point, numSegments, depth, 0 );
}
public float GetDistanceTo( Vector2 point, int numSegments = 20, int depth = 3 )
{
var p = GetClosestPositionTo( point, numSegments, depth );
return ( p - point ).Length();
}
protected float GetClosestParameterTo( float tStart, float tEnd, Vector2 position, int numSegments, int maxDepth, int currentDepth = 0 )
{
var tNormalizer = ( tEnd - tStart ) / (float) ( numSegments - 1 );
var closestIndex = -1;
var closestDistance = float.MaxValue;
var minT = 0f;
var maxT = 1f;
var startPoint = SampleAt( tStart );
var line = new Line2();
var lastT = tStart;
for ( int i = 1; i < numSegments; i++ )
{
var t = tNormalizer * i + tStart;
var nextCurvePoint = SampleAt( t );
line.Set( startPoint, nextCurvePoint );
var closestPoint = line.ClosestPointToPoint( position );
var distance = ( position - closestPoint ).LengthSquared();
if ( distance < closestDistance )
{
closestDistance = distance;
closestIndex = i - 1;
minT = lastT;
maxT = t;
}
startPoint = nextCurvePoint;
lastT = t;
}
if ( maxDepth != currentDepth )
{
return GetClosestParameterTo( minT, maxT, position, numSegments, maxDepth, currentDepth + 1 );
}
var tNormalizer2 = ( maxT - minT ) / (float)(numSegments - 1 );
closestDistance = float.MaxValue;
var closestParameter = 0.5f;
for ( int i = 0; i < numSegments; i++ )
{
var detailedT = i * tNormalizer2 + minT;
var sampledPoint = SampleAt( detailedT );
var distance = ( sampledPoint - position ).LengthSquared();
if ( distance < closestDistance )
{
closestParameter = detailedT;
closestDistance = distance;
}
}
return closestParameter;
}
}
public class CustomCurve2:Curve2
{
Func<float,Vector2> _customFunction;
public CustomCurve2( Func<float,Vector2> customFunction )
{
_customFunction = customFunction;
}
public override Vector2 SampleAt( float t )
{
return _customFunction( t );
}
}
}