RoboticArms/Library/PackageCache/com.unity.splines@d3e1e500c9a0/Editor/Controls/KnotHandles.cs
2025-11-17 15:16:36 +07:00

323 lines
14 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
namespace UnityEditor.Splines
{
static class KnotHandles
{
const float k_ColorAlphaFactor = 0.3f;
const float k_KnotRotDiscRadius = 0.18f;
const float k_KnotRotDiscWidthDefault = 1.5f;
const float k_KnotRotDiscWidthHover = 3f;
const float k_KnotHandleWidth = 2f;
static readonly List<SelectableKnot> k_KnotBuffer = new List<SelectableKnot>();
static readonly Vector3[] k_HandlePoints = new Vector3[11];
static List<(SelectableKnot knot, bool selected, bool hovered, Color knotColor, Color discColor, bool linkedKnot)> s_Knots = new ();
internal static void Do(int controlId, SelectableKnot knot, bool selected = false, bool hovered = false)
{
if (Event.current.GetTypeForControl(controlId) != EventType.Repaint)
return;
//Hovered might not be available if a TRS tool is in use
hovered &= SplineHandleUtility.IsHoverAvailableForSplineElement();
var knotColor = SplineHandleUtility.elementColor;
var rotationDiscColor = SplineHandleUtility.elementPreselectionColor;
if (hovered)
knotColor = SplineHandleUtility.elementPreselectionColor;
else if (selected)
knotColor = SplineHandleUtility.elementSelectionColor;
Draw(knot.Position, knot.Rotation, knotColor, selected, hovered, rotationDiscColor, k_KnotRotDiscWidthHover);
DrawKnotIndices(knot);
}
internal static void Draw(int controlId, SelectableKnot knot)
{
if (Event.current.GetTypeForControl(controlId) != EventType.Repaint)
return;
if(!knot.IsValid())
return;
var selected = SplineSelection.Contains(knot);
var knotHovered = SplineHandleUtility.IsElementHovered(controlId);
//Retrieving linked knots
EditorSplineUtility.GetKnotLinks(knot, k_KnotBuffer);
var drawLinkedKnotHandle = k_KnotBuffer.Count != 1;
var mainKnot = knot;
SelectableKnot lastHovered = new SelectableKnot();
// Retrieving the last hovered element
// SplineHandleUtility.lastHoveredElement is pointing either to:
// - the hovered Knot and the ID is pointing to the controlID of that knot in that case
// - if a curve is hovered, the element is the knot closest to the hovered part of the curve (start or end knot depending)
// and the controlID is the one of the curve
var lastHoveredElementIsKnot = SplineHandleUtility.lastHoveredElement is SelectableKnot;
if (lastHoveredElementIsKnot)
lastHovered = (SelectableKnot)SplineHandleUtility.lastHoveredElement;
var isCurveId = SplineHandles.IsCurveId(SplineHandleUtility.lastHoveredElementId);
var curveIsHovered = lastHoveredElementIsKnot &&
k_KnotBuffer.Contains(lastHovered) &&
isCurveId;
var hovered = knotHovered || (curveIsHovered && knot.Equals(lastHovered));
if (drawLinkedKnotHandle)
{
if (curveIsHovered)
{
drawLinkedKnotHandle = false;
if (!knot.Equals(lastHovered))
{
if (!SplineSelection.Contains(knot))
return;
hovered = false;
mainKnot = lastHovered;
}
}
else
{
foreach (var linkedKnot in k_KnotBuffer)
{
if (!hovered)
{
var kSelected = SplineSelection.Contains(linkedKnot);
// If the current knot in not selected but other linked knots are, skip rendering
if (!selected && kSelected)
return;
// If current knot is selected but not k, don't consider k as a potential knot
if (selected && !kSelected)
{
drawLinkedKnotHandle = false;
continue;
}
}
//Main knot is the older one, the one on the spline of lowest range and the knot of lowest index
if ((!SplineSelection.HasActiveSplineSelection() || SplineSelection.Contains(linkedKnot.SplineInfo)) &&
(linkedKnot.SplineInfo.Index < mainKnot.SplineInfo.Index ||
linkedKnot.SplineInfo.Index == mainKnot.SplineInfo.Index && linkedKnot.KnotIndex < mainKnot.KnotIndex))
mainKnot = linkedKnot;
}
}
}
//Hovered might not be available if a TRS tool is in use
hovered &= SplineHandleUtility.IsHoverAvailableForSplineElement();
var knotColor = SplineHandleUtility.elementColor;
var highlightColor = SplineHandleUtility.elementPreselectionColor;
var rotationDiscColor = SplineHandleUtility.elementPreselectionColor;
if (hovered)
{
knotColor = SplineHandleUtility.elementPreselectionColor;
highlightColor = SplineHandleUtility.elementPreselectionColor;
}
else if (selected)
{
knotColor = SplineHandleUtility.elementSelectionColor;
highlightColor = SplineHandleUtility.elementSelectionColor;
}
if (SplineHandleUtility.canDrawOnCurves && (hovered || selected))
{
using (new Handles.DrawingScope(highlightColor))
CurveHandles.DoCurveHighlightCap(knot);
}
if (knot.Equals(mainKnot))
{
s_Knots.Add((knot, selected, hovered, knotColor, rotationDiscColor, drawLinkedKnotHandle));
DrawKnotIndices(knot);
}
}
internal static void ClearVisibleKnots()
{
if (Event.current.type != EventType.Repaint)
return;
s_Knots.Clear();
}
internal static void DrawVisibleKnots()
{
if (Event.current.type != EventType.Repaint)
return;
foreach (var knotInfo in s_Knots)
Draw(knotInfo.knot.Position, knotInfo.knot.Rotation, knotInfo.knotColor, knotInfo.selected, knotInfo.hovered, knotInfo.discColor, k_KnotRotDiscWidthHover, knotInfo.linkedKnot);
}
static void DrawKnotIndices(SelectableKnot knot)
{
if (!SplineHandleSettings.ShowKnotIndices)
return;
var hasLinkedKnots = !(k_KnotBuffer.Count == 1 && k_KnotBuffer.Contains(knot));
if (k_KnotBuffer != null && k_KnotBuffer.Count > 0 && hasLinkedKnots)
{
var stringBuilder = new System.Text.StringBuilder("[");
for (var i = 0; i < k_KnotBuffer.Count; i++)
{
stringBuilder.Append($"({k_KnotBuffer[i].SplineInfo.Index},{k_KnotBuffer[i].KnotIndex})");
if (i != k_KnotBuffer.Count - 1)
stringBuilder.Append(", ");
}
stringBuilder.Append("]");
Handles.Label(knot.Position, stringBuilder.ToString());
}
else
{
Handles.Label(knot.Position, $"[{knot.KnotIndex}]");
}
}
internal static void Draw(SelectableKnot knot, Color knotColor, bool selected, bool hovered)
{
EditorSplineUtility.GetKnotLinks(knot, k_KnotBuffer);
var mainKnot = knot;
if(k_KnotBuffer.Count != 1)
{
foreach(var k in k_KnotBuffer)
{
//Main knot is the older one, the one on the spline of lowest range and the knot of lowest index
if(k.SplineInfo.Index < mainKnot.SplineInfo.Index ||
k.SplineInfo.Index == mainKnot.SplineInfo.Index && k.KnotIndex < mainKnot.KnotIndex)
mainKnot = k;
}
}
if(!mainKnot.Equals(knot))
return;
Draw(knot.Position, knot.Rotation, knotColor, selected, hovered, knotColor, k_KnotRotDiscWidthDefault, k_KnotBuffer.Count != 1);
}
internal static void Draw(Vector3 position, Quaternion rotation, Color knotColor, bool selected, bool hovered)
{
Draw(position, rotation, knotColor, selected, hovered, knotColor, k_KnotRotDiscWidthDefault);
}
static void UpdateHandlePoints(float size)
{
var startIndex = 5;
k_HandlePoints[startIndex] = Vector3.forward * k_KnotRotDiscRadius * size;
var r = Vector3.right * SplineHandleUtility.knotDiscRadiusFactorDefault * size;
//The first and last element should be in the middle of the points list to get a better visual
for(int i = 0; i < 9; i++)
{
var index = ( i + startIndex + 1 ) % k_HandlePoints.Length;
var pos = Quaternion.Euler(0, ( 1f - i / 8f ) * 180f, 0) * r;
k_HandlePoints[index] = pos;
if(index == k_HandlePoints.Length - 1)
{
startIndex += 1;
k_HandlePoints[0] = pos;
}
}
}
internal static void DrawInformativeKnot(SelectableKnot knot, float sizeFactor = 0.5f)
{
if (Event.current.type != EventType.Repaint)
return;
EditorSplineUtility.GetKnotLinks(knot, k_KnotBuffer);
var drawLinkedKnotHandle = k_KnotBuffer.Count != 1;
if(drawLinkedKnotHandle)
{
foreach(var k in k_KnotBuffer)
{
//If the current knot in not selected but other linked knots are, skip rendering
if(SplineSelection.Contains(k))
return;
}
}
DrawInformativeKnotVisual(knot, SplineHandleUtility.lineColor, sizeFactor);
}
static void DrawInformativeKnotVisual(SelectableKnot knot, Color knotColor, float sizeFactor = 0.5f)
{
var position = knot.Position;
var size = HandleUtility.GetHandleSize(position);
using(new Handles.DrawingScope(knotColor, Matrix4x4.TRS(position, knot.Rotation, Vector3.one)))
{
Handles.DrawSolidDisc(Vector3.zero, Vector3.up, size * SplineHandleUtility.knotDiscRadiusFactorSelected * sizeFactor);
}
}
static void Draw(Vector3 position, Quaternion rotation, Color knotColor, bool selected, bool hovered, Color discColor, float rotationDiscWidth, bool linkedKnots = false)
{
var size = HandleUtility.GetHandleSize(position);
using (new ZTestScope(CompareFunction.Less))
{
using (new Handles.DrawingScope(knotColor, Matrix4x4.TRS(position, rotation, Vector3.one)))
DrawKnotShape(size, selected, linkedKnots);
if (hovered)
{
using (new Handles.DrawingScope(discColor, Matrix4x4.TRS(position, rotation, Vector3.one)))
SplineHandleUtility.DrawAAWireDisc(Vector3.zero, Vector3.up, k_KnotRotDiscRadius * size, rotationDiscWidth);
}
}
using (new ZTestScope(CompareFunction.Greater))
{
var newKnotColor = new Color(knotColor.r, knotColor.g, knotColor.b, knotColor.a * k_ColorAlphaFactor);
using (new Handles.DrawingScope(newKnotColor, Matrix4x4.TRS(position, rotation, Vector3.one)))
DrawKnotShape(size, selected, linkedKnots);
if (hovered)
{
var newDiscColor = new Color(discColor.r, discColor.g,
discColor.b, discColor.a * k_ColorAlphaFactor);
using (new Handles.DrawingScope(newDiscColor, Matrix4x4.TRS(position, rotation, Vector3.one)))
SplineHandleUtility.DrawAAWireDisc(Vector3.zero, Vector3.up, k_KnotRotDiscRadius * size, rotationDiscWidth);
}
}
}
static void DrawKnotShape(float size, bool selected, bool linkedKnots)
{
if (!linkedKnots)
{
UpdateHandlePoints(size);
Handles.DrawAAPolyLine(SplineHandleUtility.denseLineAATex, k_KnotHandleWidth, k_HandlePoints);
if (selected)
Handles.DrawAAConvexPolygon(k_HandlePoints);
}
else
{
// Knot disc
if (selected)
{
var radius = selected ? SplineHandleUtility.knotDiscRadiusFactorSelected : SplineHandleUtility.knotDiscRadiusFactorHover;
Handles.DrawSolidDisc(Vector3.zero, Vector3.up, radius * size);
}
else
Handles.DrawWireDisc(Vector3.zero, Vector3.up, SplineHandleUtility.knotDiscRadiusFactorDefault * size, SplineHandleUtility.handleWidth * SplineHandleUtility.aliasedLineSizeMultiplier);
}
Handles.DrawAAPolyLine(Vector3.zero, Vector3.up * 2f * SplineHandleUtility.sizeFactor * size);
}
}
}