Skip to content

Commit 35b5a90

Browse files
committed
- Added "Insert Point At Cursor" option to BezierPoints' Inspector to quickly add points at clicked positions on the spline
- Added FindNearestPointToLine function to BezierSpline - Slightly improved runtime performance
1 parent fee99ee commit 35b5a90

File tree

3 files changed

+265
-50
lines changed

3 files changed

+265
-50
lines changed

Plugins/BezierSolution/BezierPoint.cs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using UnityEngine;
1+
//#define IMMEDIATE_TRANSFORM_CHANGED_CHECKS // Less performant method but updates spline values immediately (rather than in LateUpdate)
2+
3+
using UnityEngine;
24

35
namespace BezierSolution
46
{
@@ -86,9 +88,10 @@ public Vector3 position
8688
{
8789
get
8890
{
91+
#if IMMEDIATE_TRANSFORM_CHANGED_CHECKS
8992
if( transform.hasChanged )
90-
Revalidate();
91-
93+
Refresh();
94+
#endif
9295
return m_position;
9396
}
9497
set
@@ -186,9 +189,10 @@ public Vector3 precedingControlPointPosition
186189
{
187190
get
188191
{
192+
#if IMMEDIATE_TRANSFORM_CHANGED_CHECKS
189193
if( transform.hasChanged )
190-
Revalidate();
191-
194+
Refresh();
195+
#endif
192196
return m_precedingControlPointPosition;
193197
}
194198
set
@@ -199,6 +203,8 @@ public Vector3 precedingControlPointPosition
199203
if( transform.hasChanged )
200204
{
201205
m_position = transform.position;
206+
m_followingControlPointPosition = transform.TransformPoint( m_followingControlPointLocalPosition );
207+
202208
transform.hasChanged = false;
203209
}
204210

@@ -254,9 +260,10 @@ public Vector3 followingControlPointPosition
254260
{
255261
get
256262
{
263+
#if IMMEDIATE_TRANSFORM_CHANGED_CHECKS
257264
if( transform.hasChanged )
258-
Revalidate();
259-
265+
Refresh();
266+
#endif
260267
return m_followingControlPointPosition;
261268
}
262269
set
@@ -267,6 +274,8 @@ public Vector3 followingControlPointPosition
267274
if( transform.hasChanged )
268275
{
269276
m_position = transform.position;
277+
m_precedingControlPointPosition = transform.TransformPoint( m_precedingControlPointLocalPosition );
278+
270279
transform.hasChanged = false;
271280
}
272281

@@ -339,7 +348,7 @@ public void CopyTo( BezierPoint other )
339348
other.m_followingControlPointLocalPosition = m_followingControlPointLocalPosition;
340349
}
341350

342-
private void Revalidate()
351+
public void Refresh()
343352
{
344353
m_position = transform.position;
345354
m_precedingControlPointPosition = transform.TransformPoint( m_precedingControlPointLocalPosition );
@@ -348,6 +357,14 @@ private void Revalidate()
348357
transform.hasChanged = false;
349358
}
350359

360+
internal void RefreshIfChanged()
361+
{
362+
#if !IMMEDIATE_TRANSFORM_CHANGED_CHECKS
363+
if( transform.hasChanged )
364+
Refresh();
365+
#endif
366+
}
367+
351368
[System.Diagnostics.Conditional( "UNITY_EDITOR" )]
352369
private void SetSplineDirty()
353370
{

Plugins/BezierSolution/BezierSpline.cs

Lines changed: 87 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,31 @@ public struct PointIndexTuple
2121
{
2222
public readonly BezierPoint point1, point2;
2323
public readonly int index1, index2;
24-
public readonly float t;
24+
public readonly float localT;
2525

26-
public PointIndexTuple( BezierSpline spline, int index1, int index2, float t )
26+
public PointIndexTuple( BezierSpline spline, int index1, int index2, float localT )
2727
{
2828
this.point1 = spline[index1];
2929
this.point2 = spline[index2];
3030
this.index1 = index1;
3131
this.index2 = index2;
32-
this.t = t;
32+
this.localT = localT;
33+
}
34+
35+
public float GetNormalizedT()
36+
{
37+
return GetNormalizedT( localT );
38+
}
39+
40+
public float GetNormalizedT( float localT )
41+
{
42+
BezierSpline spline = point1.GetComponentInParent<BezierSpline>();
43+
return ( index1 + localT ) / ( spline.loop ? spline.Count : ( spline.Count - 1 ) );
3344
}
3445

3546
public Vector3 GetPoint()
3647
{
37-
return GetPoint( t );
48+
return GetPoint( localT );
3849
}
3950

4051
public Vector3 GetPoint( float localT )
@@ -49,7 +60,7 @@ public Vector3 GetPoint( float localT )
4960

5061
public Vector3 GetTangent()
5162
{
52-
return GetTangent( t );
63+
return GetTangent( localT );
5364
}
5465

5566
public Vector3 GetTangent( float localT )
@@ -63,7 +74,7 @@ public Vector3 GetTangent( float localT )
6374

6475
public Vector3 GetNormal()
6576
{
66-
return GetNormal( t );
77+
return GetNormal( localT );
6778
}
6879

6980
public Vector3 GetNormal( float localT )
@@ -85,7 +96,7 @@ public Vector3 GetNormal( float localT )
8596

8697
public BezierPoint.ExtraData GetExtraData()
8798
{
88-
return defaultExtraDataLerpFunction( point1.extraData, point2.extraData, t );
99+
return defaultExtraDataLerpFunction( point1.extraData, point2.extraData, localT );
89100
}
90101

91102
public BezierPoint.ExtraData GetExtraData( float localT )
@@ -95,7 +106,7 @@ public BezierPoint.ExtraData GetExtraData( float localT )
95106

96107
public BezierPoint.ExtraData GetExtraData( ExtraDataLerpFunction lerpFunction )
97108
{
98-
return lerpFunction( point1.extraData, point2.extraData, t );
109+
return lerpFunction( point1.extraData, point2.extraData, localT );
99110
}
100111

101112
public BezierPoint.ExtraData GetExtraData( float localT, ExtraDataLerpFunction lerpFunction )
@@ -156,15 +167,20 @@ private void Awake()
156167
Refresh();
157168
}
158169

159-
#if UNITY_EDITOR
160-
private void OnTransformChildrenChanged()
170+
private void LateUpdate()
161171
{
162-
Refresh();
172+
for( int i = 0; i < endPoints.Count; i++ )
173+
endPoints[i].RefreshIfChanged();
174+
175+
#if UNITY_EDITOR
176+
Internal_CheckDirty();
177+
#endif
163178
}
164179

165-
private void LateUpdate()
180+
#if UNITY_EDITOR
181+
private void OnTransformChildrenChanged()
166182
{
167-
Internal_CheckDirty();
183+
Refresh();
168184
}
169185

170186
internal void Internal_CheckDirty()
@@ -650,6 +666,51 @@ public Vector3 FindNearestPointTo( Vector3 worldPos, out float normalizedT, floa
650666
return result;
651667
}
652668

669+
public Vector3 FindNearestPointToLine( Vector3 lineStart, Vector3 lineEnd, float accuracy = 100f )
670+
{
671+
Vector3 pointOnLine;
672+
float normalizedT;
673+
return FindNearestPointToLine( lineStart, lineEnd, out pointOnLine, out normalizedT, accuracy );
674+
}
675+
676+
public Vector3 FindNearestPointToLine( Vector3 lineStart, Vector3 lineEnd, out Vector3 pointOnLine, out float normalizedT, float accuracy = 100f )
677+
{
678+
Vector3 result = Vector3.zero;
679+
pointOnLine = Vector3.zero;
680+
normalizedT = -1f;
681+
682+
float step = AccuracyToStepSize( accuracy );
683+
684+
// Find closest point on line
685+
// Credit: https://github.com/Unity-Technologies/UnityCsReference/blob/61f92bd79ae862c4465d35270f9d1d57befd1761/Editor/Mono/HandleUtility.cs#L115-L128
686+
Vector3 lineDirection = lineEnd - lineStart;
687+
float length = lineDirection.magnitude;
688+
Vector3 normalizedLineDirection = lineDirection;
689+
if( length > .000001f )
690+
normalizedLineDirection /= length;
691+
692+
float minDistance = Mathf.Infinity;
693+
for( float i = 0f; i < 1f; i += step )
694+
{
695+
Vector3 thisPoint = GetPoint( i );
696+
697+
// Find closest point on line
698+
// Credit: https://github.com/Unity-Technologies/UnityCsReference/blob/61f92bd79ae862c4465d35270f9d1d57befd1761/Editor/Mono/HandleUtility.cs#L115-L128
699+
Vector3 closestPointOnLine = lineStart + normalizedLineDirection * Mathf.Clamp( Vector3.Dot( normalizedLineDirection, thisPoint - lineStart ), 0f, length );
700+
701+
float thisDistance = ( closestPointOnLine - thisPoint ).sqrMagnitude;
702+
if( thisDistance < minDistance )
703+
{
704+
minDistance = thisDistance;
705+
result = thisPoint;
706+
pointOnLine = closestPointOnLine;
707+
normalizedT = i;
708+
}
709+
}
710+
711+
return result;
712+
}
713+
653714
// Credit: https://gamedev.stackexchange.com/a/27138
654715
public Vector3 MoveAlongSpline( ref float normalizedT, float deltaMovement, int accuracy = 3 )
655716
{
@@ -662,6 +723,9 @@ public Vector3 MoveAlongSpline( ref float normalizedT, float deltaMovement, int
662723

663724
public void ConstructLinearPath()
664725
{
726+
for( int i = 0; i < endPoints.Count; i++ )
727+
endPoints[i].RefreshIfChanged();
728+
665729
for( int i = 0; i < endPoints.Count; i++ )
666730
{
667731
endPoints[i].handleMode = BezierPoint.HandleMode.Free;
@@ -685,7 +749,10 @@ public void ConstructLinearPath()
685749
public void AutoConstructSpline()
686750
{
687751
for( int i = 0; i < endPoints.Count; i++ )
752+
{
688753
endPoints[i].handleMode = BezierPoint.HandleMode.Mirrored;
754+
endPoints[i].RefreshIfChanged();
755+
}
689756

690757
int n = endPoints.Count - 1;
691758
if( n == 1 )
@@ -769,6 +836,9 @@ public void AutoConstructSpline2()
769836
return;
770837
}
771838

839+
for( int i = 0; i < endPoints.Count; i++ )
840+
endPoints[i].RefreshIfChanged();
841+
772842
for( int i = 0; i < endPoints.Count; i++ )
773843
{
774844
Vector3 pMinus1, p1, p2;
@@ -824,6 +894,9 @@ public void AutoCalculateNormals( bool flipNormals )
824894
const float DELTA_T = 0.025f;
825895
const float ONE_MINUS_DELTA_T = 1f - DELTA_T;
826896

897+
for( int i = 0; i < endPoints.Count; i++ )
898+
endPoints[i].RefreshIfChanged();
899+
827900
for( int i = 0; i < endPoints.Count; i++ )
828901
{
829902
if( i < endPoints.Count - 1 )
@@ -921,7 +994,7 @@ IEnumerator IEnumerable.GetEnumerator()
921994
}
922995

923996
#if UNITY_EDITOR
924-
public void Reset()
997+
internal void Reset()
925998
{
926999
for( int i = endPoints.Count - 1; i >= 0; i-- )
9271000
UnityEditor.Undo.DestroyObjectImmediate( endPoints[i].gameObject );

0 commit comments

Comments
 (0)