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 points; public LerpCurve3( List 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(); 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(); 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 Subdivide( float distance, bool close ) { var output = new List(); 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(); 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(); 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; } } }