WuhuIslandTesting/Library/PackageCache/com.unity.splines@1.0.1/Editor/GUI/SplineDataHandles.cs
2025-01-07 02:06:59 +01:00

286 lines
11 KiB
C#

using System;
using System.Linq;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Splines;
namespace UnityEditor.Splines
{
/// <summary>
/// Provides methods for drawing <see cref="SplineData"/> manipulation handles.
/// </summary>
public static class SplineDataHandles
{
const float k_HandleSize = 0.15f;
const int k_PickRes = 2;
static int[] s_DataPointsIDs;
static int s_NewDataPointIndex = -1;
static bool s_AddingDataPoint = false;
static bool m_ShowAddHandle;
static float3 m_Position;
static float m_T;
/// <summary>
/// Draw default manipulation handles that enables adding, removing and moving
/// DataPoints of the targeted SplineData along a Spline. Left click on an empty location
/// on the spline adds a new DataPoint in the SplineData. Left click on an existing DataPoint
/// allows to move this point along the Spline while a right click on it allows to delete that DataPoint.
/// </summary>
/// <param name="spline">The Spline to use to interpret the SplineData.</param>
/// <param name="splineData">The SplineData for which the handles are drawn.</param>
/// <typeparam name="TSpline">The Spline type.</typeparam>
/// <typeparam name="TData">The type of data this data point stores.</typeparam>
public static void DataPointHandles<TSpline, TData>(
this TSpline spline,
SplineData<TData> splineData)
where TSpline : ISpline
{
var evt = Event.current;
if(evt.type == EventType.MouseMove)
{
//Compute distance to spline and closest point
var ray = HandleUtility.GUIPointToWorldRay(evt.mousePosition);
var distance = SplineUtility.GetNearestPoint(spline, ray, out m_Position, out m_T);
m_ShowAddHandle = distance < HandleUtility.GetHandleSize(m_Position);
}
//Id has to be consistent no matter the distance test
var id = GUIUtility.GetControlID(FocusType.Passive);
//Only activating the tooling when close enough from the spline
if(m_ShowAddHandle)
DataPointAddHandle(id, spline, splineData, m_Position, m_T);
//Remove DataPoint functionality
TryRemoveDataPoint(splineData);
//Draw Default manipulation handles
DataPointMoveHandles(spline, splineData);
}
static void TryRemoveDataPoint<TData>(SplineData<TData> splineData)
{
var evt = Event.current;
//Remove data point only when not adding one and when using right click button
if(!s_AddingDataPoint && GUIUtility.hotControl == 0
&& evt.type == EventType.MouseDown && evt.button == 1
&& s_DataPointsIDs.Contains(HandleUtility.nearestControl))
{
var dataPointIndex = splineData.Indexes.ElementAt(Array.IndexOf(s_DataPointsIDs, HandleUtility.nearestControl));
splineData.RemoveDataPoint(dataPointIndex);
evt.Use();
}
}
static void DataPointAddHandle<TSpline, TData>(
int controlID,
TSpline spline,
SplineData<TData> splineData,
float3 pos,
float t)
where TSpline : ISpline
{
Event evt = Event.current;
EventType eventType = evt.GetTypeForControl(controlID);
switch (eventType)
{
case EventType.Layout:
{
if(!Tools.viewToolActive)
HandleUtility.AddControl(controlID, HandleUtility.DistanceToCircle(pos, 0.1f));
break;
}
case EventType.Repaint:
if(HandleUtility.nearestControl == controlID && GUIUtility.hotControl == 0 || s_AddingDataPoint)
{
var upDir = spline.EvaluateUpVector(t);
Handles.CircleHandleCap(controlID, pos, Quaternion.LookRotation(upDir), 0.15f * HandleUtility.GetHandleSize(pos), EventType.Repaint);
}
break;
case EventType.MouseDown:
if (evt.button == 0
&& HandleUtility.nearestControl == controlID
&& GUIUtility.hotControl == 0)
{
s_AddingDataPoint = true;
var index = SplineUtility.ConvertIndexUnit(
spline, t,
splineData.PathIndexUnit);
s_NewDataPointIndex = splineData.AddDataPointWithDefaultValue(index);
evt.Use();
}
break;
case EventType.MouseDrag:
if (evt.button == 0 && s_AddingDataPoint)
{
GUIUtility.hotControl = 0;
var index = SplineUtility.ConvertIndexUnit(
spline, t,
splineData.PathIndexUnit);
s_NewDataPointIndex = splineData.MoveDataPoint(s_NewDataPointIndex, index);
evt.Use();
}
break;
case EventType.MouseUp:
if (evt.button == 0 && s_AddingDataPoint)
{
s_AddingDataPoint = false;
s_NewDataPointIndex = -1;
GUIUtility.hotControl = 0;
evt.Use();
}
break;
case EventType.MouseMove:
HandleUtility.Repaint();
break;
}
}
static void DataPointMoveHandles<TSpline, TData>(TSpline spline, SplineData<TData> splineData)
where TSpline : ISpline
{
if(s_DataPointsIDs == null || s_DataPointsIDs.Length != splineData.Count)
s_DataPointsIDs = new int[splineData.Count];
//Cache all data point IDs
for(int dataIndex = 0; dataIndex < splineData.Count; dataIndex++)
s_DataPointsIDs[dataIndex] = GUIUtility.GetControlID(FocusType.Passive);
//Draw all data points handles on the spline
for(int dataIndex = 0; dataIndex < splineData.Count; dataIndex++)
{
var id = GUIUtility.GetControlID(FocusType.Passive);
var index = splineData.Indexes.ElementAt(dataIndex);
SplineDataHandle(
s_DataPointsIDs[dataIndex],
spline,
splineData,
index,
k_HandleSize,
out float newIndex);
if(GUIUtility.hotControl == s_DataPointsIDs[dataIndex])
{
var newDataIndex = splineData.MoveDataPoint(dataIndex, newIndex);
//If the current DataPoint is moved across another DataPoint, then update the hotControl ID
if(newDataIndex - index != 0)
GUIUtility.hotControl = s_DataPointsIDs[newDataIndex];
}
}
}
static void SplineDataHandle<TSpline, TData>(
int controlID,
TSpline spline,
SplineData<TData> splineData,
float dataPointIndex,
float size,
out float newTime) where TSpline : ISpline
{
newTime = dataPointIndex;
Event evt = Event.current;
EventType eventType = evt.GetTypeForControl(controlID);
var normalizedT = SplineUtility.GetNormalizedInterpolation(spline, dataPointIndex, splineData.PathIndexUnit);
var dataPosition = SplineUtility.EvaluatePosition(spline, normalizedT);
switch (eventType)
{
case EventType.Layout:
var dist = HandleUtility.DistanceToCircle(dataPosition, size * HandleUtility.GetHandleSize(dataPosition));
HandleUtility.AddControl(controlID, dist);
break;
case EventType.Repaint:
DrawSplineDataHandle(controlID, dataPosition, size);
break;
case EventType.MouseDown:
if (evt.button == 0
&& HandleUtility.nearestControl == controlID
&& GUIUtility.hotControl == 0)
{
GUIUtility.hotControl = controlID;
newTime = GetClosestSplineDataT(spline, splineData);
evt.Use();
}
break;
case EventType.MouseDrag:
if (GUIUtility.hotControl == controlID)
{
newTime = GetClosestSplineDataT(spline, splineData);
evt.Use();
}
break;
case EventType.MouseUp:
if (GUIUtility.hotControl == controlID)
{
if(evt.button == 0)
{
GUIUtility.hotControl = 0;
newTime = GetClosestSplineDataT(spline, splineData);
}
evt.Use();
}
break;
case EventType.MouseMove:
HandleUtility.Repaint();
break;
}
}
static void DrawSplineDataHandle(int controlID, Vector3 position, float size)
{
var handleColor = Handles.color;
if(controlID == GUIUtility.hotControl)
handleColor = Handles.selectedColor;
else if(GUIUtility.hotControl == 0 && controlID == HandleUtility.nearestControl)
handleColor = Handles.preselectionColor;
// to avoid affecting the sphere dimensions with the handles matrix, we'll just use the position and reset
// the matrix to identity when drawing.
position = Handles.matrix * position;
using(new Handles.DrawingScope(handleColor, Matrix4x4.identity))
{
Handles.SphereHandleCap(
controlID,
position,
Quaternion.identity,
size * HandleUtility.GetHandleSize(position),
EventType.Repaint
);
}
}
// Spline must be in world space
static float GetClosestSplineDataT<TSpline,TData>(TSpline spline, SplineData<TData> splineData) where TSpline : ISpline
{
var evt = Event.current;
var ray = HandleUtility.GUIPointToWorldRay(evt.mousePosition);
SplineUtility.GetNearestPoint(spline,
ray,
out float3 _,
out float t,
k_PickRes);
return SplineUtility.ConvertIndexUnit(spline, t, splineData.PathIndexUnit);
}
}
}