199 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			C#
		
	
	
	
			
		
		
	
	
			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 );
 | |
|     }
 | |
|   }
 | |
| } |