Add focus selection to curve editor and apply margin around shown curve section

#2455
This commit is contained in:
Wojtek Figat
2025-01-29 23:54:41 +01:00
parent 0e058b2590
commit 4d6282a5b4

View File

@@ -674,30 +674,84 @@ namespace FlaxEditor.GUI
OnEditingEnd(); OnEditingEnd();
} }
/// <inheritdoc /> private void ShowCurve(bool selectedOnly)
public override void ShowWholeCurve()
{ {
if (_points.Count == 0) if (_points.Count == 0)
return; return;
_mainPanel.GetDesireClientArea(out var mainPanelArea); int pass = 1;
ViewScale = ApplyUseModeMask(EnableZoom, mainPanelArea.Size / _contents.Size, ViewScale); REDO:
Float2 minPos = Float2.Maximum;
// Get curve bounds in Keyframes (time and value)
Float2 posMin = Float2.Maximum, posMax = Float2.Minimum;
// TODO: include bezier curve bounds calculation to handle curve outside the bounds made out of points
foreach (var point in _points) foreach (var point in _points)
{ {
var pos = point.PointToParent(point.Location); if (selectedOnly && !point.IsSelected)
Float2.Min(ref minPos, ref pos, out minPos); continue;
var pos = point.Point;
Float2.Min(ref posMin, ref pos, out posMin);
Float2.Max(ref posMax, ref pos, out posMax);
} }
var minPosPoint = _contents.PointToParent(ref minPos);
var scroll = new Float2(_mainPanel.HScrollBar?.TargetValue ?? 0, _mainPanel.VScrollBar?.TargetValue ?? 0); // Apply margin around the area
scroll = ApplyUseModeMask(EnablePanning, minPosPoint, scroll); var posMargin = (posMax - posMin) * 0.05f;
if (_mainPanel.HScrollBar != null) posMin -= posMargin;
_mainPanel.HScrollBar.TargetValue = scroll.X; posMax += posMargin;
if (_mainPanel.VScrollBar != null)
_mainPanel.VScrollBar.TargetValue = scroll.Y; // Convert from Keyframes to Contents
_mainPanel.GetDesireClientArea(out var viewRect);
PointFromKeyframesToContents(ref posMin, ref viewRect);
PointFromKeyframesToContents(ref posMax, ref viewRect);
var tmp = posMin;
Float2.Min(ref posMin, ref posMax, out posMin);
Float2.Max(ref posMax, ref tmp, out posMax);
var contentsSize = posMax - posMin;
// Convert from Contents to Main Panel
posMin = _contents.PointToParent(posMin);
posMax = _contents.PointToParent(posMax);
tmp = posMin;
Float2.Min(ref posMin, ref posMax, out posMin);
Float2.Max(ref posMax, ref tmp, out posMax);
// Update zoom (leave unchanged when focusing a single point)
var zoomMask = EnableZoom;
if (Mathf.IsZero(posMargin.X))
zoomMask &= ~UseMode.Horizontal;
if (Mathf.IsZero(posMargin.Y))
zoomMask &= ~UseMode.Vertical;
ViewScale = ApplyUseModeMask(zoomMask, viewRect.Size / contentsSize, ViewScale);
// Update scroll (attempt to center the area when it's smaller than the view)
Float2 viewOffset = -posMin;
Float2 viewSize = _mainPanel.Size;
Float2 viewSizeLeft = viewSize - Float2.Clamp(posMax - posMin, Float2.Zero, viewSize);
viewOffset += viewSizeLeft * 0.5f;
viewOffset = ApplyUseModeMask(EnablePanning, viewOffset, _mainPanel.ViewOffset);
_mainPanel.ViewOffset = viewOffset;
// Do it multiple times so the view offset can be properly calculate once the view scale gets changes
if (pass++ <= 2)
goto REDO;
UpdateKeyframes(); UpdateKeyframes();
} }
/// <summary>
/// Focuses the view on the selected keyframes.
/// </summary>
public void FocusSelection()
{
// Fallback to showing whole curve if nothing is selected
ShowCurve(SelectionCount != 0);
}
/// <inheritdoc />
public override void ShowWholeCurve()
{
ShowCurve(false);
}
/// <inheritdoc /> /// <inheritdoc />
public override void Evaluate(out object result, float time, bool loop = false) public override void Evaluate(out object result, float time, bool loop = false)
{ {
@@ -774,10 +828,7 @@ namespace FlaxEditor.GUI
point = _contents.PointFromParent(point); point = _contents.PointFromParent(point);
// Contents -> Keyframes // Contents -> Keyframes
return new Float2( return PointFromContentsToKeyframes(ref point, ref curveContentAreaBounds);
(point.X + _contents.Location.X) / UnitsPerSecond,
(point.Y + _contents.Location.Y - curveContentAreaBounds.Height) / -UnitsPerSecond
);
} }
/// <summary> /// <summary>
@@ -789,10 +840,7 @@ namespace FlaxEditor.GUI
protected Float2 PointFromKeyframes(Float2 point, ref Rectangle curveContentAreaBounds) protected Float2 PointFromKeyframes(Float2 point, ref Rectangle curveContentAreaBounds)
{ {
// Keyframes -> Contents // Keyframes -> Contents
point = new Float2( PointFromKeyframesToContents(ref point, ref curveContentAreaBounds);
point.X * UnitsPerSecond - _contents.Location.X,
point.Y * -UnitsPerSecond + curveContentAreaBounds.Height - _contents.Location.Y
);
// Contents -> Main Panel // Contents -> Main Panel
point = _contents.PointToParent(point); point = _contents.PointToParent(point);
@@ -801,6 +849,22 @@ namespace FlaxEditor.GUI
return _mainPanel.PointToParent(point); return _mainPanel.PointToParent(point);
} }
internal Float2 PointFromContentsToKeyframes(ref Float2 point, ref Rectangle curveContentAreaBounds)
{
return new Float2(
(point.X + _contents.Location.X) / UnitsPerSecond,
(point.Y + _contents.Location.Y - curveContentAreaBounds.Height) / -UnitsPerSecond
);
}
internal void PointFromKeyframesToContents(ref Float2 point, ref Rectangle curveContentAreaBounds)
{
point = new Float2(
point.X * UnitsPerSecond - _contents.Location.X,
point.Y * -UnitsPerSecond + curveContentAreaBounds.Height - _contents.Location.Y
);
}
private void DrawAxis(Float2 axis, Rectangle viewRect, float min, float max, float pixelRange) private void DrawAxis(Float2 axis, Rectangle viewRect, float min, float max, float pixelRange)
{ {
Utilities.Utils.DrawCurveTicks((decimal tick, float strength) => Utilities.Utils.DrawCurveTicks((decimal tick, float strength) =>
@@ -947,7 +1011,7 @@ namespace FlaxEditor.GUI
} }
else if (options.FocusSelection.Process(this)) else if (options.FocusSelection.Process(this))
{ {
ShowWholeCurve(); FocusSelection();
return true; return true;
} }