Files
2026-07-13 13:13:17 +08:00

304 lines
12 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#nullable enable
using System.Runtime.CompilerServices;
using ImGuiNET;
using T3.Core.DataTypes.Vector;
using T3.Core.Utils;
using T3.Editor.Gui.Styling;
using T3.Editor.Gui.UiHelpers;
namespace T3.Editor.Gui.Interaction;
/// <remarks>
/// Terminology
/// valueRange - delta value for complete revolution of current dial
/// tickInterval = Log10 delta vale between ticks.
/// </remarks>
[HelpUiID("InfinitySlider")]
internal static class InfinitySliderOverlay
{
private const int Log10YDistance = 100;
private const float Width = 750;
internal static void Draw(ref double roundedValue,
bool restarted,
Vector2 center,
double min = double.NegativeInfinity,
double max = double.PositiveInfinity,
float scale = 0.1f,
bool clampMin = false,
bool clampMax = false,
bool disableRounding = false)
{
var drawList = ImGui.GetForegroundDrawList();
_io = ImGui.GetIO();
if (restarted)
{
_value = roundedValue;
_center = _io.MousePos;
_verticalDistance = 50;
//_dampedAngleVelocity = 0;
_dampedModifierScaleFactor = 1;
_lastXOffset = 0;
_originalValue = roundedValue;
_isManipulating = false;
}
// Update value...
var mousePosX = (int)(_io.MousePos.X * 2) / 2;
var xOffset = mousePosX - _center.X;
var deltaX = xOffset - _lastXOffset;
if (MathF.Abs(xOffset) > UserSettings.Config.ClickThreshold)
{
_isManipulating = true;
}
_lastXOffset = xOffset;
if (ImGui.IsMouseClicked(ImGuiMouseButton.Right))
{
_dragCenterStart = _center;
}
var isDraggingWidgetPosition = ImGui.IsMouseDown(ImGuiMouseButton.Right);
if (isDraggingWidgetPosition)
{
FrameStats.Current.OpenedPopupCapturedMouse = true;
_center = _dragCenterStart + ImGui.GetMouseDragDelta(ImGuiMouseButton.Right);
}
else
{
_verticalDistance = _center.Y - _io.MousePos.Y;
}
//_dampedAngleVelocity = MathUtils.Lerp(_dampedAngleVelocity, (float)deltaX, 0.06f);
// Update radius and value range
var normalizedLogDistanceForLog10 = (_verticalDistance / T3Ui.UiScaleFactor) / Log10YDistance;
// Value range and tick interval
_dampedModifierScaleFactor = MathUtils.Lerp(_dampedModifierScaleFactor, GetKeyboardScaleFactor(), 0.1f);
var valueRange = (Math.Pow(10, normalizedLogDistanceForLog10)) * scale * 0.25f * _dampedModifierScaleFactor * 600;
// Update value...
if (!isDraggingWidgetPosition)
{
_value += deltaX / Width / T3Ui.UiScaleFactor * valueRange;
}
_value = MathUtils.OptionalClamp(_value, min, clampMin, max, clampMax);
DrawUi(out roundedValue, min, max, valueRange, mousePosX, drawList,disableRounding);
roundedValue = MathUtils.OptionalClamp(roundedValue, min, clampMin, max, clampMax);
if (!_isManipulating)
roundedValue = _originalValue;
}
private static bool DrawUi(out double roundedValue, double min, double max, double valueRange, int mousePosX,
ImDrawListPtr drawList, bool disableRounding)
{
var log10 = Math.Log10(valueRange);
var iLog10 = Math.Floor(log10);
var logRemainder = log10 - iLog10;
var tickValueInterval = Math.Pow(10, iLog10 - 1) ;
var roundInterval = logRemainder switch
{
> 0.8 => tickValueInterval / 1,
_ => tickValueInterval / 10
};
roundedValue = (_io.KeyCtrl || disableRounding) ? _value : Math.Round(_value / roundInterval) * roundInterval;
var rSize = new Vector2(Width, 40) * T3Ui.UiScaleFactor;
var rCenter = new Vector2(mousePosX, _io.MousePos.Y - rSize.Y);
var rect = new ImRect(rCenter - rSize / 2, rCenter + rSize / 2);
var numberOfTicks = valueRange / tickValueInterval;
var valueTickRemainder = MathUtils.Fmod(_value, tickValueInterval);
// Draw scale range indicates
{
for (var yIndex = -2; yIndex < 4; yIndex++)
{
var centerPoint = new Vector2(mousePosX, _center.Y - Log10YDistance * yIndex * T3Ui.UiScaleFactor);
var v = Math.Pow(10, yIndex);
var label = $"× {v:G5}";
var size = ImGui.CalcTextSize(label);
var fade = (MathF.Abs(centerPoint.Y - rCenter.Y) / 50).Clamp(0, 1);
var boxSize = new Vector2(80, 100) * T3Ui.UiScaleFactor;
var labelCenter = centerPoint + new Vector2(10) * T3Ui.UiScaleFactor;
drawList.AddRectFilled(centerPoint - boxSize / 2, centerPoint + boxSize / 2 + new Vector2(0, -1), UiColors.BackgroundFull.Fade(0.3f * fade),
3);
drawList.AddText(
labelCenter - new Vector2(size.X / 2 + 10, 18),
UiColors.ForegroundFull.Fade(fade * 0.6f),
label);
}
}
// Draw ticks with labels
drawList.AddRectFilled(rCenter - rSize / 2, rCenter + rSize / 2, UiColors.BackgroundFull.Fade(0.8f), 8);
for (var tickIndex = -(int)numberOfTicks / 2; tickIndex < numberOfTicks / 2; tickIndex++)
{
var f = MathF.Pow(MathF.Abs(tickIndex / ((float)numberOfTicks / 2)), 2f);
var negF = 1 - f;
var valueAtTick = tickIndex * tickValueInterval + _value - valueTickRemainder;
GetXForValueIfVisible(valueAtTick, valueRange, mousePosX, Width, out var tickX);
var xFactor = 5;
var isPrimary = Math.Abs(MathUtils.Fmod(valueAtTick + tickValueInterval * xFactor, tickValueInterval * 10) - tickValueInterval * xFactor) <
tickValueInterval / 10;
var isPrimary2 =
Math.Abs(MathUtils.Fmod(valueAtTick + tickValueInterval * xFactor * 10, tickValueInterval * 100) - tickValueInterval * xFactor * 10) <
tickValueInterval / 100;
var fff = MathUtils.SmootherStep(1, 0.8f, (float)logRemainder);
drawList.AddLine(
new Vector2(tickX, rect.Max.Y),
new Vector2(tickX, rect.Max.Y - 10 * T3Ui.UiScaleFactor),
UiColors.ForegroundFull.Fade(negF * (isPrimary ? 1 : 0.5f * fff)),
1
);
var ff = (1 - (float)logRemainder * 2);
if (isPrimary2 || ff < 1)
{
var font = isPrimary2 ? Fonts.FontBold : Fonts.FontNormal;
var v = Math.Abs(valueAtTick) < 0.0001 ? 0 : valueAtTick;
var label = $"{v:G5}";
ImGui.PushFont(font);
var size = ImGui.CalcTextSize(label);
ImGui.PopFont();
drawList.AddText(font,
font.FontSize,
new Vector2(tickX - 1 - size.X / 2,
rect.Max.Y - 30 * T3Ui.UiScaleFactor),
UiColors.BackgroundFull.Fade(negF * ff),
label);
drawList.AddText(font,
font.FontSize,
new Vector2(tickX - size.X / 2 + 1,
rect.Max.Y - 30 * T3Ui.UiScaleFactor),
UiColors.BackgroundFull.Fade(negF * ff),
label);
var fadeOut = (isPrimary ? 1 : ff) * 0.7f;
drawList.AddText(font,
font.FontSize,
new Vector2(tickX - size.X / 2,
rect.Max.Y - 30 * T3Ui.UiScaleFactor),
UiColors.ForegroundFull.Fade(negF * (isPrimary2 ? 1 : fadeOut)),
label);
}
}
// Draw Value range
{
var rangeMin = _value - valueRange / 2;
var rangeMax = _value + valueRange / 2;
if (min > -9999999 && max < 9999999 && !(min < rangeMin && max < rangeMin || (min > rangeMax && max > rangeMax)))
{
var visibleMinValue = Math.Max(min, rangeMin);
var visibleMaxValue = Math.Min(max, rangeMax);
var y = rect.Max.Y - 1.5f;
GetXForValueIfVisible(visibleMinValue, valueRange, mousePosX, Width, out var visibleMinX);
GetXForValueIfVisible(visibleMaxValue, valueRange, mousePosX, Width, out var visibleMaxX);
drawList.AddLine(new Vector2(visibleMinX, y),
new Vector2(visibleMaxX, y),
UiColors.ForegroundFull.Fade(0.5f),
2);
}
}
// Current value at mouse
{
if (!GetXForValueIfVisible(roundedValue, valueRange, mousePosX, Width, out var screenX))
return true;
ImGui.PushFont(Fonts.FontLarge);
var label = $"{roundedValue:G7}\n";
var labelSize = ImGui.CalcTextSize(label);
drawList.AddRectFilled(
new Vector2(screenX - labelSize.X / 2 - 10 * T3Ui.UiScaleFactor, rect.Max.Y),
new Vector2(screenX + labelSize.X / 2 + 10 * T3Ui.UiScaleFactor, rect.Max.Y + 35 * T3Ui.UiScaleFactor),
UiColors.BackgroundFull.Fade(0.8f),
5
);
drawList.AddLine(new Vector2(screenX, rect.Min.Y),
new Vector2(screenX, rect.Max.Y + 5 * T3Ui.UiScaleFactor),
UiColors.StatusActivated,
1
);
drawList.AddText(new Vector2(screenX - labelSize.X / 2, rect.Max.Y + 3 * T3Ui.UiScaleFactor),
Color.White.Fade(1),
label
);
ImGui.PopFont();
}
// Draw previous value
{
if (GetXForValueIfVisible(_originalValue, valueRange, mousePosX, Width, out var visibleMinX))
{
var y = rect.Min.Y;
drawList.AddTriangleFilled(new Vector2(visibleMinX, y + 4),
new Vector2(visibleMinX + 4, y),
new Vector2(visibleMinX - 4, y),
UiColors.ForegroundFull);
}
}
return false;
}
private static bool IsValueVisible(double value, double valueRange)
{
return Math.Abs(_value - value) <= valueRange / 2 + 0.001;
}
private static float GetXForValue(double value, double valueRange, float mousePosX)
{
return (float)((_value - value) / valueRange + mousePosX);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool GetXForValueIfVisible(double v, double valueRange, float mouseX, float width, out float x)
{
x = float.NaN;
if (!IsValueVisible(v, valueRange))
return false;
x = MathF.Floor((float)((v - _value) / valueRange * width * T3Ui.UiScaleFactor + mouseX));
return true;
}
private static double GetKeyboardScaleFactor()
{
if (_io.KeyAlt)
return 10;
if (_io.KeyShift)
return 0.1;
return 1;
}
/** The precise value before rounding. This used for all internal calculations. */
private static double _value;
private static float _verticalDistance;
private static Vector2 _center = Vector2.Zero;
private static Vector2 _dragCenterStart = Vector2.Zero;
private static double _lastXOffset;
private static double _dampedModifierScaleFactor;
private static double _originalValue;
private static bool _isManipulating;
private static ImGuiIOPtr _io;
}