Skip to content

Commit

Permalink
- Added 'Quick Edit Mode" to BezierSpline: in this mode, new points c…
Browse files Browse the repository at this point in the history
…an quickly be added/inserted to the spline and the existing points can be dragged around/snapped to the scene geometry

- Added BezierSpline.GeneratePointCache function to calculate evenly spaced points along the spline. Instead of calling the function with default parameters, use the 'pointCache' property instead which is cached and is updated automatically when spline is modified. If a spline is rarely modified at runtime, then point cache can be used to get points, tangents, normals, etc. along the spline in a cheaper and uniform way
- Added an optional second pass to FindNearestPointTo functions for greatly increased accuracy
- Added 'Invert Spline' button to BezierSpline's context menu which inverts the order of the end points
- Added 'version' property to BezierSpline which is automatically increased whenever the spline's properties change
- Added per-setting Reset buttons to the plugin's settings
- Removed GUILayer and FlareLayer components from demo scene
  • Loading branch information
yasirkula committed Jun 17, 2021
1 parent d083ef6 commit 8504d6a
Show file tree
Hide file tree
Showing 14 changed files with 1,161 additions and 484 deletions.
28 changes: 20 additions & 8 deletions .github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ The user interface for the spline editor should be pretty self-explanatory with

![inspector](Images/BezierSpline.png)

When **Quick Edit Mode** is enabled, new points can quickly be added/inserted to the spline and the existing points can be dragged around/snapped to the scene geometry.

To reverse the order of the end points in a spline, you can right click the BezierSpline component and click the *Invert Spline* button.

You can tweak the Scene view gizmos via *Project Settings/yasirkula/Bezier Solution* page (on older versions, this menu is located at *Preferences* window).

![gizmo-settings](Images/GizmoSettings.png)
Expand Down Expand Up @@ -112,6 +116,8 @@ If auto calculated normals don't look quite right despite modifying the "normalA

You can register to the spline's **onSplineChanged** event to get notified when some of its properties have changed. This event has the following signature: `delegate void SplineChangeDelegate( BezierSpline spline, DirtyFlags dirtyFlags )`. **DirtyFlags** is an enum flag, meaning that it can have one or more of these values: **SplineShapeChanged**, **NormalsChanged** and/or **ExtraDataChanged**. *SplineShapeChanged* flag means that either the spline's Transform values have changed or some of its end points' Transform values have changed (changing control points may also trigger this flag). *NormalsChanged* flag means that normals of some of the end points have changed and *ExtraDataChanged* flag means that extraDatas of some of the end points have changed.

BezierSpline also has a **version** property which is automatically increased whenever the spline's properties change.

**NOTE:** onSplineChanged event is usually invoked in *LateUpdate*. Before it is invoked, *autoConstructMode* and *autoCalculateNormals* properties' values are checked and the relevant auto construction/calculation functions are executed if necessary.

## UTILITY FUNCTIONS
Expand Down Expand Up @@ -148,11 +154,11 @@ Calculates the approximate length of a segment of the spline. To calculate the l

Returns the two end points that are closest to *normalizedT*. The *Segment* struct also holds a *localT* value in range \[0,1\], which can be used to interpolate between the properties of these two end points. You can also call the `GetPoint()`, `GetTangent()`, `GetNormal()` and `GetExtraData()` functions of this struct and the returned values will be calculated as if the spline consisted of only these two end points.

- `Vector3 FindNearestPointTo( Vector3 worldPos, out float normalizedT, float accuracy = 100f )`
- `Vector3 FindNearestPointTo( Vector3 worldPos, out float normalizedT, float accuracy = 100f, int secondPassIterations = 7, float secondPassExtents = 0.025f )`

Finds the nearest point on the spline to any given point in 3D space. The normalizedT parameter is optional and it returns the parameter *t* corresponding to the resulting point. To find the nearest point, the spline is divided into "accuracy" points and the nearest point is selected. Thus, the result will not be 100% accurate but will be good enough for casual use-cases.
Finds the nearest point on the spline to any given point in 3D space. The normalizedT parameter is optional and it returns the parameter *t* corresponding to the resulting point. To find the nearest point, the spline is divided into "accuracy" points and the nearest point is selected. Then, a binary search is performed in "secondPassIterations" steps in range `[normalizedT-secondPassExtents, normalizedT+secondPassExtents]` to fine-tune the result.

- `Vector3 FindNearestPointToLine( Vector3 lineStart, Vector3 lineEnd, out Vector3 pointOnLine, out float normalizedT, float accuracy = 100f )`
- `Vector3 FindNearestPointToLine( Vector3 lineStart, Vector3 lineEnd, out Vector3 pointOnLine, out float normalizedT, float accuracy = 100f, int secondPassIterations = 7, float secondPassExtents = 0.025f )`

Finds the nearest point on the spline to the given line in 3D space. The pointOnLine and normalizedT parameters are optional.

Expand All @@ -162,17 +168,23 @@ Moves a point (normalizedT) on the spline deltaMovement units ahead and returns

- `EvenlySpacedPointsHolder CalculateEvenlySpacedPoints( float resolution = 10f, float accuracy = 3f )`

Finds uniformly distributed points on the spline and returns a lookup table. The lookup table isn't refreshed automatically, so it may be invalidated when the spline is modified. This function's resolution parameter determines approximately how many points will be calculated per each segment of the spline and accuracy determines how accurate the uniform spacing will be. The default values should work well in most cases.
Finds uniformly distributed points along the spline and returns a lookup table. The lookup table isn't refreshed automatically, so it may be invalidated when the spline is modified. This function's *resolution* parameter determines approximately how many points will be calculated per each segment of the spline and accuracy determines how accurate the uniform spacing will be. The default values should work well in most cases.

**Food For Thought**: BezierSpline has an **evenlySpacedPoints** property which is a shorthand for `CalculateEvenlySpacedPoints()`. Its value is cached and won't be recalculated unless the spline is modified.

**EvenlySpacedPointsHolder** struct has *spline*, *splineLength* and *uniformNormalizedTs* variables. In addition, it has the following convenience functions:
*EvenlySpacedPointsHolder* class has *spline*, *splineLength* and *uniformNormalizedTs* variables. In addition, it has the following convenience functions:

- **GetNormalizedTAtPercentage:** converts a percentage to normalizedT value, i.e. if you enter 0.5f as parameter, it will return the normalizedT value of the spline that corresponds to its actual middle point.
- **GetNormalizedTAtDistance:** finds the normalizedT value that is specified units away from the spline's starting point.
- **GetPercentageAtNormalizedT:** inverse of *GetNormalizedTAtPercentage*.

- `PointCache GeneratePointCache( EvenlySpacedPointsHolder lookupTable, ExtraDataLerpFunction extraDataLerpFunction, PointCacheFlags cachedData = PointCacheFlags.All, int resolution = 100 )`

**GetNormalizedTAtPercentage:** converts a percentage to normalizedT value, i.e. if you enter 0.5f as parameter, it will return the normalizedT value of the spline that corresponds to its actual middle point.
Returns a cache of data for uniformly distributed points along the spline. The cache isn't refreshed automatically, so it may be invalidated when the spline is modified. This function's *resolution* parameter determines how many uniformly distributed points the cache will have. To determine which data should be cached, *cachedData* parameter is used. *PointCacheFlags* is an enum flag, meaning that it can have one or more of these values: **Positions**, **Normals**, **Tangents**, **Bitangents** and/or **ExtraDatas**. *lookupTable* is an optional parameter and, by default, spline's *evenlySpacedPoints* is used. *extraDataLerpFunction* is also an optional parameter and is used only when PointCacheFlags.ExtraDatas is included in cachedData.

**GetNormalizedTAtDistance:** finds the normalizedT value that is specified units away from the spline's starting point.
**Food For Thought**: BezierSpline has a **pointCache** property which is a shorthand for `GeneratePointCache()`. Its value is cached and won't be recalculated unless the spline is modified.

**GetPercentageAtNormalizedT:** inverse of *GetNormalizedTAtPercentage*.
*PointCache* class has *positions*, *normals*, *tangents*, *bitangents*, *extraDatas* and *loop* variables (loop determines whether or not the spline had its *loop* property set to true while calculating the cache). In addition, it has the following functions: *GetPoint*, *GetNormal*, *GetTangent*, *GetBitangent* and *GetExtraData* (if the required data for a function wasn't included in PointCacheFlags, then the function will throw an exception). If a spline is rarely modified at runtime, then point cache can be used to get points, tangents, normals, etc. along the spline in a cheaper and uniform way.

## OTHER COMPONENTS

Expand Down
75 changes: 10 additions & 65 deletions Plugins/BezierSolution/BezierPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,72 +3,8 @@
namespace BezierSolution
{
[AddComponentMenu( "Bezier Solution/Bezier Point" )]
public class BezierPoint : MonoBehaviour
public partial class BezierPoint : MonoBehaviour
{
[System.Serializable]
public struct ExtraData
{
public float c1, c2, c3, c4;

public ExtraData( float c1 = 0f, float c2 = 0f, float c3 = 0f, float c4 = 0f )
{
this.c1 = c1;
this.c2 = c2;
this.c3 = c3;
this.c4 = c4;
}

public static ExtraData Lerp( ExtraData a, ExtraData b, float t )
{
t = Mathf.Clamp01( t );
return new ExtraData(
a.c1 + ( b.c1 - a.c1 ) * t,
a.c2 + ( b.c2 - a.c2 ) * t,
a.c3 + ( b.c3 - a.c3 ) * t,
a.c4 + ( b.c4 - a.c4 ) * t );
}

public static ExtraData LerpUnclamped( ExtraData a, ExtraData b, float t )
{
return new ExtraData(
a.c1 + ( b.c1 - a.c1 ) * t,
a.c2 + ( b.c2 - a.c2 ) * t,
a.c3 + ( b.c3 - a.c3 ) * t,
a.c4 + ( b.c4 - a.c4 ) * t );
}

public static implicit operator ExtraData( Vector2 v ) { return new ExtraData( v.x, v.y ); }
public static implicit operator ExtraData( Vector3 v ) { return new ExtraData( v.x, v.y, v.z ); }
public static implicit operator ExtraData( Vector4 v ) { return new ExtraData( v.x, v.y, v.z, v.w ); }
public static implicit operator ExtraData( Quaternion q ) { return new ExtraData( q.x, q.y, q.z, q.w ); }
public static implicit operator ExtraData( Rect r ) { return new ExtraData( r.xMin, r.yMin, r.width, r.height ); }
#if UNITY_2017_2_OR_NEWER
public static implicit operator ExtraData( Vector2Int v ) { return new ExtraData( v.x, v.y ); }
public static implicit operator ExtraData( Vector3Int v ) { return new ExtraData( v.x, v.y, v.z ); }
public static implicit operator ExtraData( RectInt r ) { return new ExtraData( r.xMin, r.yMin, r.width, r.height ); }
#endif

public static implicit operator Vector2( ExtraData v ) { return new Vector2( v.c1, v.c2 ); }
public static implicit operator Vector3( ExtraData v ) { return new Vector3( v.c1, v.c2, v.c3 ); }
public static implicit operator Vector4( ExtraData v ) { return new Vector4( v.c1, v.c2, v.c3, v.c4 ); }
public static implicit operator Quaternion( ExtraData v ) { return new Quaternion( v.c1, v.c2, v.c3, v.c4 ); }
public static implicit operator Rect( ExtraData v ) { return new Rect( v.c1, v.c2, v.c3, v.c4 ); }
#if UNITY_2017_2_OR_NEWER
public static implicit operator Vector2Int( ExtraData v ) { return new Vector2Int( Mathf.RoundToInt( v.c1 ), Mathf.RoundToInt( v.c2 ) ); }
public static implicit operator Vector3Int( ExtraData v ) { return new Vector3Int( Mathf.RoundToInt( v.c1 ), Mathf.RoundToInt( v.c2 ), Mathf.RoundToInt( v.c3 ) ); }
public static implicit operator RectInt( ExtraData v ) { return new RectInt( Mathf.RoundToInt( v.c1 ), Mathf.RoundToInt( v.c2 ), Mathf.RoundToInt( v.c3 ), Mathf.RoundToInt( v.c4 ) ); }
#endif

public static bool operator ==( ExtraData d1, ExtraData d2 ) { return d1.c1 == d2.c1 && d1.c2 == d2.c2 && d1.c3 == d2.c3 && d1.c4 == d2.c4; }
public static bool operator !=( ExtraData d1, ExtraData d2 ) { return d1.c1 != d2.c1 || d1.c2 != d2.c2 || d1.c3 != d2.c3 || d1.c4 != d2.c4; }

public override bool Equals( object obj ) { return obj is ExtraData && this == (ExtraData) obj; }
public override int GetHashCode() { return unchecked((int) ( ( ( ( 17 * 23 + c1 ) * 23 + c2 ) * 23 + c3 ) * 23 + c4 )); }
public override string ToString() { return ( (Vector4) this ).ToString(); }
}

public enum HandleMode { Free, Aligned, Mirrored };

public Vector3 localPosition
{
get { return transform.localPosition; }
Expand Down Expand Up @@ -435,5 +371,14 @@ public void Reset()

transform.hasChanged = true;
}

#if UNITY_EDITOR
[ContextMenu( "Invert Spline" )]
private void InvertSplineContextMenu()
{
if( spline )
spline.InvertSpline( "Invert spline" );
}
#endif
}
}
Loading

0 comments on commit 8504d6a

Please sign in to comment.