1007 lines
34 KiB
C#
1007 lines
34 KiB
C#
using System;
|
|
using System.Numerics;
|
|
using Dalamud.Bindings.ImGui;
|
|
using Dalamud.Interface;
|
|
using Dalamud.Interface.Utility;
|
|
using LightlessSync.UI;
|
|
|
|
// imagine putting this goober name here
|
|
|
|
namespace LightlessSync.UI.Style;
|
|
|
|
public enum SeluneGradientMode
|
|
{
|
|
Vertical,
|
|
Horizontal,
|
|
Both,
|
|
}
|
|
|
|
public enum SeluneHighlightMode
|
|
{
|
|
Horizontal,
|
|
Vertical,
|
|
Both,
|
|
Point,
|
|
}
|
|
|
|
public sealed class SeluneGradientSettings
|
|
{
|
|
public Vector4 GradientColor { get; init; } = UIColors.Get("LightlessPurple");
|
|
public Vector4? HighlightColor { get; init; }
|
|
public float GradientPeakOpacity { get; init; } = 0.07f;
|
|
public float HighlightPeakAlpha { get; init; } = 0.13f;
|
|
public float HighlightEdgeAlpha { get; init; } = 0f;
|
|
public float HighlightMidpoint { get; init; } = 0.45f;
|
|
public float MinimumHighlightHalfHeight { get; init; } = 25f;
|
|
public float MinimumHighlightHalfWidth { get; init; } = 25f;
|
|
public float HighlightFadeInSpeed { get; init; } = 14f;
|
|
public float HighlightFadeOutSpeed { get; init; } = 8f;
|
|
public float HighlightBorderThickness { get; init; } = 10f;
|
|
public float HighlightBorderRounding { get; init; } = 8f;
|
|
public SeluneGradientMode BackgroundMode { get; init; } = SeluneGradientMode.Vertical;
|
|
public SeluneHighlightMode HighlightMode { get; init; } = SeluneHighlightMode.Horizontal;
|
|
|
|
public static SeluneGradientSettings Default
|
|
=> new()
|
|
{
|
|
GradientColor = UIColors.Get("LightlessPurple"),
|
|
HighlightColor = UIColors.Get("LightlessPurple"),
|
|
};
|
|
}
|
|
|
|
public static class Selune
|
|
{
|
|
[ThreadStatic] private static SeluneCanvas? _activeCanvas;
|
|
|
|
public static SeluneCanvas Begin(SeluneBrush brush, ImDrawListPtr drawList, Vector2 windowPos, Vector2 windowSize, SeluneGradientSettings? settings = null)
|
|
{
|
|
var canvas = new SeluneCanvas(brush, drawList, windowPos, windowSize, settings ?? SeluneGradientSettings.Default, _activeCanvas);
|
|
_activeCanvas = canvas;
|
|
return canvas;
|
|
}
|
|
|
|
internal static void Release(SeluneCanvas canvas)
|
|
{
|
|
if (_activeCanvas == canvas)
|
|
_activeCanvas = canvas.Previous;
|
|
}
|
|
|
|
public static void RegisterHighlight(
|
|
Vector2 rectMin,
|
|
Vector2 rectMax,
|
|
SeluneHighlightMode? modeOverride = null,
|
|
bool borderOnly = false,
|
|
float? borderThicknessOverride = null,
|
|
bool exactSize = false,
|
|
bool clipToElement = false,
|
|
Vector2? clipPadding = null,
|
|
float? roundingOverride = null,
|
|
bool spanFullWidth = false,
|
|
Vector4? highlightColorOverride = null,
|
|
float? highlightAlphaOverride = null)
|
|
=> _activeCanvas?.RegisterHighlight(
|
|
rectMin,
|
|
rectMax,
|
|
modeOverride,
|
|
borderOnly,
|
|
borderThicknessOverride,
|
|
exactSize,
|
|
clipToElement,
|
|
clipPadding,
|
|
roundingOverride,
|
|
spanFullWidth,
|
|
highlightColorOverride,
|
|
highlightAlphaOverride);
|
|
}
|
|
|
|
public sealed class SeluneBrush
|
|
{
|
|
private Vector2? _highlightCenter;
|
|
private Vector2 _highlightHalfSize;
|
|
private SeluneHighlightMode _highlightMode;
|
|
private bool _highlightBorderOnly;
|
|
private float _borderThickness;
|
|
private bool _useClipRect;
|
|
private Vector2 _clipMin;
|
|
private Vector2 _clipMax;
|
|
private float _highlightRounding;
|
|
private bool _highlightUsedThisFrame;
|
|
private float _highlightIntensity;
|
|
private Vector4? _highlightColorOverride;
|
|
private float? _highlightAlphaOverride;
|
|
|
|
internal void BeginFrame()
|
|
{
|
|
_highlightUsedThisFrame = false;
|
|
}
|
|
|
|
internal void RegisterHighlight(Vector2 center, Vector2 halfSize, SeluneHighlightMode mode, bool borderOnly, float borderThickness, bool useClipRect, Vector2 clipMin, Vector2 clipMax, float rounding, Vector4? highlightColorOverride, float? highlightAlphaOverride)
|
|
{
|
|
if (halfSize.X <= 0f || halfSize.Y <= 0f)
|
|
return;
|
|
|
|
_highlightUsedThisFrame = true;
|
|
_highlightCenter = center;
|
|
_highlightHalfSize = halfSize;
|
|
_highlightMode = mode;
|
|
_highlightBorderOnly = borderOnly;
|
|
_borderThickness = borderOnly ? Math.Max(borderThickness, 0f) : 0f;
|
|
_useClipRect = useClipRect;
|
|
_clipMin = clipMin;
|
|
_clipMax = clipMax;
|
|
_highlightRounding = rounding;
|
|
_highlightColorOverride = highlightColorOverride;
|
|
_highlightAlphaOverride = highlightAlphaOverride;
|
|
}
|
|
|
|
internal void UpdateFade(float deltaTime, SeluneGradientSettings settings)
|
|
{
|
|
if (deltaTime <= 0f)
|
|
return;
|
|
|
|
if (_highlightUsedThisFrame)
|
|
{
|
|
_highlightIntensity = MathF.Min(1f, _highlightIntensity + deltaTime * settings.HighlightFadeInSpeed);
|
|
}
|
|
else
|
|
{
|
|
_highlightIntensity = MathF.Max(0f, _highlightIntensity - deltaTime * settings.HighlightFadeOutSpeed);
|
|
|
|
if (_highlightIntensity <= 0.001f)
|
|
{
|
|
ResetHighlightState();
|
|
}
|
|
}
|
|
}
|
|
|
|
internal SeluneHighlightRenderState GetRenderState()
|
|
=> new(
|
|
_highlightCenter,
|
|
_highlightHalfSize,
|
|
_highlightMode,
|
|
_highlightBorderOnly,
|
|
_borderThickness,
|
|
_useClipRect,
|
|
_clipMin,
|
|
_clipMax,
|
|
_highlightRounding,
|
|
_highlightIntensity,
|
|
_highlightColorOverride,
|
|
_highlightAlphaOverride);
|
|
|
|
private void ResetHighlightState()
|
|
{
|
|
_highlightCenter = null;
|
|
_highlightHalfSize = Vector2.Zero;
|
|
_highlightBorderOnly = false;
|
|
_borderThickness = 0f;
|
|
_useClipRect = false;
|
|
_highlightRounding = 0f;
|
|
_highlightColorOverride = null;
|
|
_highlightAlphaOverride = null;
|
|
}
|
|
}
|
|
|
|
internal readonly struct SeluneHighlightRenderState
|
|
{
|
|
public SeluneHighlightRenderState(
|
|
Vector2? center,
|
|
Vector2 halfSize,
|
|
SeluneHighlightMode mode,
|
|
bool borderOnly,
|
|
float borderThickness,
|
|
bool useClipRect,
|
|
Vector2 clipMin,
|
|
Vector2 clipMax,
|
|
float rounding,
|
|
float intensity,
|
|
Vector4? colorOverride,
|
|
float? alphaOverride)
|
|
{
|
|
Center = center;
|
|
HalfSize = halfSize;
|
|
Mode = mode;
|
|
BorderOnly = borderOnly;
|
|
BorderThickness = borderThickness;
|
|
UseClipRect = useClipRect;
|
|
ClipMin = clipMin;
|
|
ClipMax = clipMax;
|
|
Rounding = rounding;
|
|
Intensity = intensity;
|
|
ColorOverride = colorOverride;
|
|
AlphaOverride = alphaOverride;
|
|
}
|
|
|
|
public Vector2? Center { get; }
|
|
public Vector2 HalfSize { get; }
|
|
public SeluneHighlightMode Mode { get; }
|
|
public bool BorderOnly { get; }
|
|
public float BorderThickness { get; }
|
|
public bool UseClipRect { get; }
|
|
public Vector2 ClipMin { get; }
|
|
public Vector2 ClipMax { get; }
|
|
public float Rounding { get; }
|
|
public float Intensity { get; }
|
|
public Vector4? ColorOverride { get; }
|
|
public float? AlphaOverride { get; }
|
|
|
|
public bool HasHighlight => Center.HasValue && HalfSize.X > 0f && HalfSize.Y > 0f && Intensity > 0.001f;
|
|
}
|
|
|
|
public sealed class SeluneCanvas : IDisposable
|
|
{
|
|
private readonly SeluneBrush _brush;
|
|
private readonly ImDrawListPtr _drawList;
|
|
private readonly Vector2 _windowPos;
|
|
private readonly Vector2 _windowSize;
|
|
private readonly SeluneGradientSettings _settings;
|
|
private bool _fadeUpdatedThisFrame;
|
|
|
|
internal SeluneCanvas? Previous { get; }
|
|
|
|
internal SeluneCanvas(SeluneBrush brush, ImDrawListPtr drawList, Vector2 windowPos, Vector2 windowSize, SeluneGradientSettings settings, SeluneCanvas? previous)
|
|
{
|
|
_brush = brush;
|
|
_drawList = drawList;
|
|
_windowPos = windowPos;
|
|
_windowSize = windowSize;
|
|
_settings = settings;
|
|
Previous = previous;
|
|
_fadeUpdatedThisFrame = false;
|
|
|
|
_brush.BeginFrame();
|
|
}
|
|
|
|
public void DrawGradient(float gradientTopY, float gradientBottomY, float deltaTime)
|
|
{
|
|
DrawInternal(gradientTopY, gradientBottomY, deltaTime, true, true);
|
|
}
|
|
|
|
public void DrawHighlightOnly(float gradientTopY, float gradientBottomY, float deltaTime)
|
|
{
|
|
DrawInternal(gradientTopY, gradientBottomY, deltaTime, false, true);
|
|
}
|
|
|
|
public void DrawHighlightOnly(float deltaTime)
|
|
=> DrawHighlightOnly(_windowPos.Y, _windowPos.Y + _windowSize.Y, deltaTime);
|
|
|
|
public void Animate(float deltaTime)
|
|
{
|
|
UpdateFadeOnce(deltaTime);
|
|
}
|
|
|
|
private void UpdateFadeOnce(float deltaTime)
|
|
{
|
|
if (_fadeUpdatedThisFrame)
|
|
return;
|
|
|
|
_brush.UpdateFade(deltaTime, _settings);
|
|
_fadeUpdatedThisFrame = true;
|
|
}
|
|
|
|
private void DrawInternal(float gradientTopY, float gradientBottomY, float deltaTime, bool drawBackground, bool drawHighlight)
|
|
{
|
|
UpdateFadeOnce(deltaTime);
|
|
|
|
SeluneRenderer.DrawGradient(
|
|
_drawList,
|
|
_windowPos,
|
|
_windowSize,
|
|
gradientTopY,
|
|
gradientBottomY,
|
|
_brush.GetRenderState(),
|
|
_settings,
|
|
drawBackground,
|
|
drawHighlight);
|
|
}
|
|
|
|
internal void RegisterHighlight(
|
|
Vector2 rectMin,
|
|
Vector2 rectMax,
|
|
SeluneHighlightMode? modeOverride,
|
|
bool borderOnly,
|
|
float? borderThicknessOverride,
|
|
bool exactSize,
|
|
bool clipToElement,
|
|
Vector2? clipPadding,
|
|
float? roundingOverride,
|
|
bool spanFullWidth,
|
|
Vector4? highlightColorOverride,
|
|
float? highlightAlphaOverride)
|
|
{
|
|
if (spanFullWidth)
|
|
{
|
|
rectMin.X = _windowPos.X;
|
|
rectMax.X = _windowPos.X + _windowSize.X;
|
|
}
|
|
|
|
var size = rectMax - rectMin;
|
|
if (size.X <= 0f || size.Y <= 0f)
|
|
return;
|
|
|
|
var center = rectMin + size * 0.5f;
|
|
var halfWidth = exactSize ? size.X * 0.5f : Math.Max(size.X * 0.5f, _settings.MinimumHighlightHalfWidth * ImGuiHelpers.GlobalScale);
|
|
var halfHeight = exactSize ? size.Y * 0.5f : Math.Max(size.Y * 0.5f, _settings.MinimumHighlightHalfHeight * ImGuiHelpers.GlobalScale);
|
|
var mode = modeOverride ?? _settings.HighlightMode;
|
|
var thickness = borderOnly ? (borderThicknessOverride ?? _settings.HighlightBorderThickness) * ImGuiHelpers.GlobalScale : 0f;
|
|
var useClip = clipToElement;
|
|
var padding = clipPadding ?? (borderOnly && thickness > 0f
|
|
? new Vector2(thickness)
|
|
: Vector2.Zero);
|
|
var clipMin = rectMin - padding;
|
|
var clipMax = rectMax + padding;
|
|
var rounding = (roundingOverride ?? _settings.HighlightBorderRounding) * ImGuiHelpers.GlobalScale;
|
|
|
|
_brush.RegisterHighlight(center, new Vector2(halfWidth, halfHeight), mode, borderOnly, thickness, useClip, clipMin, clipMax, rounding, highlightColorOverride, highlightAlphaOverride);
|
|
_fadeUpdatedThisFrame = false;
|
|
}
|
|
|
|
public void Dispose()
|
|
=> Selune.Release(this);
|
|
}
|
|
// i wonder which sync will copy this shitty code now
|
|
internal static class SeluneRenderer
|
|
{
|
|
public static void DrawGradient(
|
|
ImDrawListPtr drawList,
|
|
Vector2 windowPos,
|
|
Vector2 windowSize,
|
|
float gradientTopY,
|
|
float gradientBottomY,
|
|
SeluneHighlightRenderState highlightState,
|
|
SeluneGradientSettings settings,
|
|
bool drawBackground = true,
|
|
bool drawHighlight = true)
|
|
{
|
|
var gradientLeft = windowPos.X;
|
|
var gradientRight = windowPos.X + windowSize.X;
|
|
var windowBottomY = windowPos.Y + windowSize.Y;
|
|
var clampedTopY = MathF.Max(gradientTopY, windowPos.Y);
|
|
var clampedBottomY = MathF.Min(gradientBottomY, windowBottomY);
|
|
|
|
if (clampedBottomY <= clampedTopY)
|
|
return;
|
|
|
|
var color = settings.GradientColor;
|
|
var topColorVec = new Vector4(color.X, color.Y, color.Z, 0f);
|
|
var bottomColorVec = new Vector4(color.X, color.Y, color.Z, 0f);
|
|
var midColorVec = new Vector4(color.X, color.Y, color.Z, settings.GradientPeakOpacity);
|
|
|
|
if (drawBackground)
|
|
{
|
|
DrawBackground(
|
|
drawList,
|
|
gradientLeft,
|
|
gradientRight,
|
|
clampedTopY,
|
|
clampedBottomY,
|
|
topColorVec,
|
|
midColorVec,
|
|
bottomColorVec,
|
|
settings.BackgroundMode);
|
|
}
|
|
|
|
if (!drawHighlight)
|
|
return;
|
|
|
|
DrawHighlight(
|
|
drawList,
|
|
gradientLeft,
|
|
gradientRight,
|
|
clampedTopY,
|
|
clampedBottomY,
|
|
highlightState,
|
|
settings);
|
|
}
|
|
|
|
private static void DrawBackground(
|
|
ImDrawListPtr drawList,
|
|
float gradientLeft,
|
|
float gradientRight,
|
|
float clampedTopY,
|
|
float clampedBottomY,
|
|
Vector4 topColorVec,
|
|
Vector4 midColorVec,
|
|
Vector4 bottomColorVec,
|
|
SeluneGradientMode mode)
|
|
{
|
|
switch (mode)
|
|
{
|
|
case SeluneGradientMode.Vertical:
|
|
DrawVerticalBackground(drawList, gradientLeft, gradientRight, clampedTopY, clampedBottomY, topColorVec, midColorVec, bottomColorVec);
|
|
break;
|
|
case SeluneGradientMode.Horizontal:
|
|
DrawHorizontalBackground(drawList, gradientLeft, gradientRight, clampedTopY, clampedBottomY, topColorVec, midColorVec, bottomColorVec);
|
|
break;
|
|
case SeluneGradientMode.Both:
|
|
DrawVerticalBackground(drawList, gradientLeft, gradientRight, clampedTopY, clampedBottomY, topColorVec, midColorVec, bottomColorVec);
|
|
DrawHorizontalBackground(drawList, gradientLeft, gradientRight, clampedTopY, clampedBottomY, topColorVec, midColorVec, bottomColorVec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private static void DrawVerticalBackground(
|
|
ImDrawListPtr drawList,
|
|
float gradientLeft,
|
|
float gradientRight,
|
|
float clampedTopY,
|
|
float clampedBottomY,
|
|
Vector4 topColorVec,
|
|
Vector4 midColorVec,
|
|
Vector4 bottomColorVec)
|
|
{
|
|
var topColor = ImGui.ColorConvertFloat4ToU32(topColorVec);
|
|
var midColor = ImGui.ColorConvertFloat4ToU32(midColorVec);
|
|
var bottomColor = ImGui.ColorConvertFloat4ToU32(bottomColorVec);
|
|
|
|
var midY = clampedTopY + (clampedBottomY - clampedTopY) * 0.035f;
|
|
drawList.AddRectFilledMultiColor(
|
|
new Vector2(gradientLeft, clampedTopY),
|
|
new Vector2(gradientRight, midY),
|
|
topColor,
|
|
topColor,
|
|
midColor,
|
|
midColor);
|
|
|
|
drawList.AddRectFilledMultiColor(
|
|
new Vector2(gradientLeft, midY),
|
|
new Vector2(gradientRight, clampedBottomY),
|
|
midColor,
|
|
midColor,
|
|
bottomColor,
|
|
bottomColor);
|
|
}
|
|
|
|
private static void DrawHorizontalBackground(
|
|
ImDrawListPtr drawList,
|
|
float gradientLeft,
|
|
float gradientRight,
|
|
float clampedTopY,
|
|
float clampedBottomY,
|
|
Vector4 leftColorVec,
|
|
Vector4 midColorVec,
|
|
Vector4 rightColorVec)
|
|
{
|
|
var leftColor = ImGui.ColorConvertFloat4ToU32(leftColorVec);
|
|
var midColor = ImGui.ColorConvertFloat4ToU32(midColorVec);
|
|
var rightColor = ImGui.ColorConvertFloat4ToU32(rightColorVec);
|
|
|
|
var midX = gradientLeft + (gradientRight - gradientLeft) * 0.035f;
|
|
drawList.AddRectFilledMultiColor(
|
|
new Vector2(gradientLeft, clampedTopY),
|
|
new Vector2(midX, clampedBottomY),
|
|
leftColor,
|
|
midColor,
|
|
midColor,
|
|
leftColor);
|
|
|
|
drawList.AddRectFilledMultiColor(
|
|
new Vector2(midX, clampedTopY),
|
|
new Vector2(gradientRight, clampedBottomY),
|
|
midColor,
|
|
rightColor,
|
|
rightColor,
|
|
midColor);
|
|
}
|
|
|
|
private static void DrawHighlight(
|
|
ImDrawListPtr drawList,
|
|
float gradientLeft,
|
|
float gradientRight,
|
|
float clampedTopY,
|
|
float clampedBottomY,
|
|
SeluneHighlightRenderState highlightState,
|
|
SeluneGradientSettings settings)
|
|
{
|
|
if (!highlightState.HasHighlight)
|
|
return;
|
|
|
|
var highlightColor = highlightState.ColorOverride ?? settings.HighlightColor ?? settings.GradientColor;
|
|
var clampedIntensity = Math.Clamp(highlightState.Intensity, 0f, 1f);
|
|
var alphaScale = Math.Clamp(highlightState.AlphaOverride ?? 1f, 0f, 1f);
|
|
var peakAlpha = settings.HighlightPeakAlpha * clampedIntensity * alphaScale;
|
|
var edgeAlpha = settings.HighlightEdgeAlpha * clampedIntensity * alphaScale;
|
|
|
|
if (peakAlpha <= 0f && edgeAlpha <= 0f)
|
|
return;
|
|
|
|
var highlightEdgeVec = new Vector4(highlightColor.X, highlightColor.Y, highlightColor.Z, edgeAlpha);
|
|
var highlightPeakVec = new Vector4(highlightColor.X, highlightColor.Y, highlightColor.Z, peakAlpha);
|
|
var center = highlightState.Center!.Value;
|
|
var halfSize = highlightState.HalfSize;
|
|
|
|
if (highlightState.UseClipRect)
|
|
drawList.PushClipRect(highlightState.ClipMin, highlightState.ClipMax, true);
|
|
|
|
switch (highlightState.Mode)
|
|
{
|
|
case SeluneHighlightMode.Horizontal:
|
|
DrawHorizontalHighlight(
|
|
drawList,
|
|
gradientLeft,
|
|
gradientRight,
|
|
clampedTopY,
|
|
clampedBottomY,
|
|
center,
|
|
halfSize,
|
|
highlightEdgeVec,
|
|
highlightPeakVec,
|
|
settings.HighlightMidpoint,
|
|
highlightState.BorderOnly,
|
|
highlightState.BorderThickness,
|
|
highlightState.Rounding);
|
|
break;
|
|
|
|
case SeluneHighlightMode.Vertical:
|
|
DrawVerticalHighlight(
|
|
drawList,
|
|
gradientLeft,
|
|
gradientRight,
|
|
clampedTopY,
|
|
clampedBottomY,
|
|
center,
|
|
halfSize,
|
|
highlightEdgeVec,
|
|
highlightPeakVec,
|
|
settings.HighlightMidpoint,
|
|
highlightState.BorderOnly,
|
|
highlightState.BorderThickness,
|
|
highlightState.Rounding);
|
|
break;
|
|
|
|
case SeluneHighlightMode.Both:
|
|
DrawCombinedHighlight(
|
|
drawList,
|
|
gradientLeft,
|
|
gradientRight,
|
|
clampedTopY,
|
|
clampedBottomY,
|
|
center,
|
|
halfSize,
|
|
highlightEdgeVec,
|
|
highlightPeakVec,
|
|
highlightState.BorderOnly,
|
|
highlightState.BorderThickness,
|
|
highlightState.Rounding);
|
|
break;
|
|
|
|
case SeluneHighlightMode.Point:
|
|
DrawPointHighlight(
|
|
drawList,
|
|
center,
|
|
halfSize,
|
|
highlightEdgeVec,
|
|
highlightPeakVec,
|
|
highlightState.BorderOnly,
|
|
highlightState.BorderThickness);
|
|
break;
|
|
}
|
|
|
|
if (highlightState.UseClipRect)
|
|
drawList.PopClipRect();
|
|
}
|
|
|
|
private static void DrawHorizontalHighlight(
|
|
ImDrawListPtr drawList,
|
|
float gradientLeft,
|
|
float gradientRight,
|
|
float clampedTopY,
|
|
float clampedBottomY,
|
|
Vector2 center,
|
|
Vector2 halfSize,
|
|
Vector4 edgeColor,
|
|
Vector4 peakColor,
|
|
float midpoint,
|
|
bool borderOnly,
|
|
float borderThickness,
|
|
float rounding)
|
|
{
|
|
var highlightTop = MathF.Max(clampedTopY, center.Y - halfSize.Y);
|
|
var highlightBottom = MathF.Min(clampedBottomY, center.Y + halfSize.Y);
|
|
if (highlightBottom <= highlightTop)
|
|
return;
|
|
|
|
var highlightLeft = MathF.Max(gradientLeft, center.X - halfSize.X);
|
|
var highlightRight = MathF.Min(gradientRight, center.X + halfSize.X);
|
|
|
|
if (highlightRight <= highlightLeft || highlightBottom <= highlightTop)
|
|
return;
|
|
|
|
if (!borderOnly || borderThickness <= 0f)
|
|
{
|
|
DrawHorizontalHighlightRect(
|
|
drawList,
|
|
highlightLeft,
|
|
highlightRight,
|
|
highlightTop,
|
|
highlightBottom,
|
|
edgeColor,
|
|
peakColor,
|
|
midpoint,
|
|
1f);
|
|
return;
|
|
}
|
|
|
|
var innerTop = MathF.Min(highlightBottom, MathF.Max(highlightTop, center.Y - MathF.Max(halfSize.Y - borderThickness, 0f)));
|
|
var innerBottom = MathF.Max(highlightTop, MathF.Min(highlightBottom, center.Y + MathF.Max(halfSize.Y - borderThickness, 0f)));
|
|
var edgeU32 = ImGui.ColorConvertFloat4ToU32(edgeColor);
|
|
var peakU32 = ImGui.ColorConvertFloat4ToU32(peakColor);
|
|
|
|
if (innerTop > highlightTop)
|
|
{
|
|
drawList.AddRectFilledMultiColor(
|
|
new Vector2(highlightLeft, highlightTop),
|
|
new Vector2(highlightRight, innerTop),
|
|
edgeU32,
|
|
edgeU32,
|
|
peakU32,
|
|
peakU32);
|
|
}
|
|
|
|
if (innerBottom < highlightBottom)
|
|
{
|
|
drawList.AddRectFilledMultiColor(
|
|
new Vector2(highlightLeft, innerBottom),
|
|
new Vector2(highlightRight, highlightBottom),
|
|
peakU32,
|
|
peakU32,
|
|
edgeU32,
|
|
edgeU32);
|
|
}
|
|
}
|
|
|
|
private static void DrawVerticalHighlight(
|
|
ImDrawListPtr drawList,
|
|
float gradientLeft,
|
|
float gradientRight,
|
|
float clampedTopY,
|
|
float clampedBottomY,
|
|
Vector2 center,
|
|
Vector2 halfSize,
|
|
Vector4 edgeColor,
|
|
Vector4 peakColor,
|
|
float midpoint,
|
|
bool borderOnly,
|
|
float borderThickness,
|
|
float rounding)
|
|
{
|
|
var highlightTop = MathF.Max(clampedTopY, center.Y - halfSize.Y);
|
|
var highlightBottom = MathF.Min(clampedBottomY, center.Y + halfSize.Y);
|
|
var highlightLeft = MathF.Max(gradientLeft, center.X - halfSize.X);
|
|
var highlightRight = MathF.Min(gradientRight, center.X + halfSize.X);
|
|
|
|
if (highlightRight <= highlightLeft || highlightBottom <= highlightTop)
|
|
return;
|
|
|
|
if (!borderOnly || borderThickness <= 0f)
|
|
{
|
|
DrawVerticalHighlightRect(
|
|
drawList,
|
|
highlightLeft,
|
|
highlightRight,
|
|
highlightTop,
|
|
highlightBottom,
|
|
edgeColor,
|
|
peakColor,
|
|
midpoint,
|
|
1f);
|
|
return;
|
|
}
|
|
|
|
var innerLeft = MathF.Min(highlightRight, MathF.Max(highlightLeft, center.X - MathF.Max(halfSize.X - borderThickness, 0f)));
|
|
var innerRight = MathF.Max(highlightLeft, MathF.Min(highlightRight, center.X + MathF.Max(halfSize.X - borderThickness, 0f)));
|
|
var edgeU32 = ImGui.ColorConvertFloat4ToU32(edgeColor);
|
|
var peakU32 = ImGui.ColorConvertFloat4ToU32(peakColor);
|
|
|
|
if (innerLeft > highlightLeft)
|
|
{
|
|
drawList.AddRectFilledMultiColor(
|
|
new Vector2(highlightLeft, highlightTop),
|
|
new Vector2(innerLeft, highlightBottom),
|
|
edgeU32,
|
|
peakU32,
|
|
peakU32,
|
|
edgeU32);
|
|
}
|
|
|
|
if (innerRight < highlightRight)
|
|
{
|
|
drawList.AddRectFilledMultiColor(
|
|
new Vector2(innerRight, highlightTop),
|
|
new Vector2(highlightRight, highlightBottom),
|
|
peakU32,
|
|
edgeU32,
|
|
edgeU32,
|
|
peakU32);
|
|
}
|
|
}
|
|
|
|
private static void DrawCombinedHighlight(
|
|
ImDrawListPtr drawList,
|
|
float gradientLeft,
|
|
float gradientRight,
|
|
float clampedTopY,
|
|
float clampedBottomY,
|
|
Vector2 center,
|
|
Vector2 halfSize,
|
|
Vector4 edgeColor,
|
|
Vector4 peakColor,
|
|
bool borderOnly,
|
|
float borderThickness,
|
|
float rounding)
|
|
{
|
|
var highlightLeft = MathF.Max(gradientLeft, center.X - halfSize.X);
|
|
var highlightRight = MathF.Min(gradientRight, center.X + halfSize.X);
|
|
var highlightTop = MathF.Max(clampedTopY, center.Y - halfSize.Y);
|
|
var highlightBottom = MathF.Min(clampedBottomY, center.Y + halfSize.Y);
|
|
|
|
if (highlightRight <= highlightLeft || highlightBottom <= highlightTop)
|
|
return;
|
|
|
|
if (borderOnly && borderThickness > 0f)
|
|
{
|
|
DrawRoundedBorderGlow(drawList, center, halfSize, borderThickness, edgeColor, peakColor, rounding);
|
|
return;
|
|
}
|
|
|
|
if (!borderOnly || borderThickness <= 0f)
|
|
{
|
|
const float combinedScale = 0.85f;
|
|
DrawHorizontalHighlightRect(
|
|
drawList,
|
|
highlightLeft,
|
|
highlightRight,
|
|
highlightTop,
|
|
highlightBottom,
|
|
edgeColor,
|
|
peakColor,
|
|
0.5f,
|
|
combinedScale);
|
|
|
|
DrawVerticalHighlightRect(
|
|
drawList,
|
|
highlightLeft,
|
|
highlightRight,
|
|
highlightTop,
|
|
highlightBottom,
|
|
edgeColor,
|
|
peakColor,
|
|
0.5f,
|
|
combinedScale);
|
|
return;
|
|
}
|
|
|
|
var outerLeft = MathF.Max(gradientLeft, highlightLeft - borderThickness);
|
|
var outerRight = MathF.Min(gradientRight, highlightRight + borderThickness);
|
|
var outerTop = MathF.Max(clampedTopY, highlightTop - borderThickness);
|
|
var outerBottom = MathF.Min(clampedBottomY, highlightBottom + borderThickness);
|
|
|
|
var edge = ImGui.ColorConvertFloat4ToU32(edgeColor);
|
|
var peak = ImGui.ColorConvertFloat4ToU32(peakColor);
|
|
|
|
if (outerTop < highlightTop)
|
|
{
|
|
drawList.AddRectFilledMultiColor(
|
|
new Vector2(outerLeft, outerTop),
|
|
new Vector2(outerRight, highlightTop),
|
|
edge,
|
|
edge,
|
|
peak,
|
|
peak);
|
|
}
|
|
|
|
if (outerBottom > highlightBottom)
|
|
{
|
|
drawList.AddRectFilledMultiColor(
|
|
new Vector2(outerLeft, highlightBottom),
|
|
new Vector2(outerRight, outerBottom),
|
|
peak,
|
|
peak,
|
|
edge,
|
|
edge);
|
|
}
|
|
|
|
if (outerLeft < highlightLeft)
|
|
{
|
|
drawList.AddRectFilledMultiColor(
|
|
new Vector2(outerLeft, highlightTop),
|
|
new Vector2(highlightLeft, highlightBottom),
|
|
edge,
|
|
peak,
|
|
peak,
|
|
edge);
|
|
}
|
|
|
|
if (outerRight > highlightRight)
|
|
{
|
|
drawList.AddRectFilledMultiColor(
|
|
new Vector2(highlightRight, highlightTop),
|
|
new Vector2(outerRight, highlightBottom),
|
|
peak,
|
|
edge,
|
|
edge,
|
|
peak);
|
|
}
|
|
}
|
|
|
|
private static void DrawPointHighlight(
|
|
ImDrawListPtr drawList,
|
|
Vector2 center,
|
|
Vector2 halfSize,
|
|
Vector4 edgeColor,
|
|
Vector4 peakColor,
|
|
bool borderOnly,
|
|
float borderThickness)
|
|
{
|
|
if (halfSize.X <= 0f || halfSize.Y <= 0f)
|
|
return;
|
|
|
|
if (borderOnly && borderThickness > 0f)
|
|
{
|
|
DrawPointBorderGlow(drawList, center, halfSize, borderThickness, edgeColor, peakColor);
|
|
return;
|
|
}
|
|
|
|
const int layers = 7;
|
|
for (int layer = 0; layer < layers; layer++)
|
|
{
|
|
float t = layers <= 1 ? 1f : layer / (layers - 1f);
|
|
float scale = 1f - 0.75f * t;
|
|
var scaledHalfSize = new Vector2(MathF.Max(1f, halfSize.X * scale), MathF.Max(1f, halfSize.Y * scale));
|
|
var color = Vector4.Lerp(edgeColor, peakColor, t);
|
|
DrawEllipseFilled(drawList, center, scaledHalfSize, ImGui.ColorConvertFloat4ToU32(color));
|
|
}
|
|
}
|
|
|
|
private static void DrawPointBorderGlow(
|
|
ImDrawListPtr drawList,
|
|
Vector2 center,
|
|
Vector2 halfSize,
|
|
float thickness,
|
|
Vector4 edgeColor,
|
|
Vector4 peakColor)
|
|
{
|
|
int layers = Math.Max(6, (int)MathF.Ceiling(thickness));
|
|
for (int i = 0; i < layers; i++)
|
|
{
|
|
float t = layers <= 1 ? 1f : i / (layers - 1f);
|
|
float offset = thickness * t;
|
|
var expandedHalfSize = new Vector2(MathF.Max(1f, halfSize.X + offset), MathF.Max(1f, halfSize.Y + offset));
|
|
var color = Vector4.Lerp(peakColor, edgeColor, t);
|
|
color.W = Math.Clamp((peakColor.W * 0.8f) + (edgeColor.W - peakColor.W) * t, 0f, 1f);
|
|
DrawEllipseStroke(drawList, center, expandedHalfSize, ImGui.ColorConvertFloat4ToU32(color), 2f);
|
|
}
|
|
}
|
|
|
|
private static void DrawEllipseFilled(ImDrawListPtr drawList, Vector2 center, Vector2 halfSize, uint color, int segments = 48)
|
|
{
|
|
if (halfSize.X <= 0f || halfSize.Y <= 0f)
|
|
return;
|
|
|
|
BuildEllipsePath(drawList, center, halfSize, segments);
|
|
drawList.PathFillConvex(color);
|
|
}
|
|
|
|
private static void DrawEllipseStroke(ImDrawListPtr drawList, Vector2 center, Vector2 halfSize, uint color, float thickness, int segments = 48)
|
|
{
|
|
if (halfSize.X <= 0f || halfSize.Y <= 0f)
|
|
return;
|
|
|
|
BuildEllipsePath(drawList, center, halfSize, segments);
|
|
drawList.PathStroke(color, ImDrawFlags.None, MathF.Max(1f, thickness));
|
|
}
|
|
|
|
private static void BuildEllipsePath(ImDrawListPtr drawList, Vector2 center, Vector2 halfSize, int segments)
|
|
{
|
|
const float twoPi = MathF.PI * 2f;
|
|
segments = Math.Clamp(segments, 12, 96);
|
|
drawList.PathClear();
|
|
for (int i = 0; i < segments; i++)
|
|
{
|
|
float angle = twoPi * (i / (float)segments);
|
|
var point = new Vector2(
|
|
center.X + MathF.Cos(angle) * halfSize.X,
|
|
center.Y + MathF.Sin(angle) * halfSize.Y);
|
|
drawList.PathLineTo(point);
|
|
}
|
|
}
|
|
|
|
private static void DrawRoundedBorderGlow(
|
|
ImDrawListPtr drawList,
|
|
Vector2 center,
|
|
Vector2 halfSize,
|
|
float thickness,
|
|
Vector4 edgeColor,
|
|
Vector4 peakColor,
|
|
float rounding)
|
|
{
|
|
int layers = Math.Max(6, (int)MathF.Ceiling(thickness));
|
|
for (int i = 0; i < layers; i++)
|
|
{
|
|
float t = layers <= 1 ? 0f : i / (layers - 1f);
|
|
float offset = thickness * t;
|
|
var min = new Vector2(center.X - halfSize.X - offset, center.Y - halfSize.Y - offset);
|
|
var max = new Vector2(center.X + halfSize.X + offset, center.Y + halfSize.Y + offset);
|
|
var color = Vector4.Lerp(peakColor, edgeColor, t);
|
|
color.W = Math.Clamp((peakColor.W * 0.8f) + (edgeColor.W - peakColor.W) * t, 0f, 1f);
|
|
drawList.AddRect(min, max, ImGui.ColorConvertFloat4ToU32(color), MathF.Max(0f, rounding + offset), ImDrawFlags.RoundCornersAll, 2f);
|
|
}
|
|
}
|
|
|
|
private static void DrawHorizontalHighlightRect(
|
|
ImDrawListPtr drawList,
|
|
float left,
|
|
float right,
|
|
float top,
|
|
float bottom,
|
|
Vector4 edgeColor,
|
|
Vector4 peakColor,
|
|
float midpoint,
|
|
float alphaScale)
|
|
{
|
|
if (right <= left || bottom <= top)
|
|
return;
|
|
|
|
edgeColor.W *= alphaScale;
|
|
peakColor.W *= alphaScale;
|
|
|
|
var edge = ImGui.ColorConvertFloat4ToU32(edgeColor);
|
|
var peak = ImGui.ColorConvertFloat4ToU32(peakColor);
|
|
var highlightMid = top + (bottom - top) * midpoint;
|
|
|
|
drawList.AddRectFilledMultiColor(
|
|
new Vector2(left, top),
|
|
new Vector2(right, highlightMid),
|
|
edge,
|
|
edge,
|
|
peak,
|
|
peak);
|
|
|
|
drawList.AddRectFilledMultiColor(
|
|
new Vector2(left, highlightMid),
|
|
new Vector2(right, bottom),
|
|
peak,
|
|
peak,
|
|
edge,
|
|
edge);
|
|
}
|
|
|
|
private static void DrawVerticalHighlightRect(
|
|
ImDrawListPtr drawList,
|
|
float left,
|
|
float right,
|
|
float top,
|
|
float bottom,
|
|
Vector4 edgeColor,
|
|
Vector4 peakColor,
|
|
float midpoint,
|
|
float alphaScale)
|
|
{
|
|
if (right <= left || bottom <= top)
|
|
return;
|
|
|
|
edgeColor.W *= alphaScale;
|
|
peakColor.W *= alphaScale;
|
|
|
|
var edge = ImGui.ColorConvertFloat4ToU32(edgeColor);
|
|
var peak = ImGui.ColorConvertFloat4ToU32(peakColor);
|
|
var highlightMid = left + (right - left) * midpoint;
|
|
|
|
drawList.AddRectFilledMultiColor(
|
|
new Vector2(left, top),
|
|
new Vector2(highlightMid, bottom),
|
|
edge,
|
|
peak,
|
|
peak,
|
|
edge);
|
|
|
|
drawList.AddRectFilledMultiColor(
|
|
new Vector2(highlightMid, top),
|
|
new Vector2(right, bottom),
|
|
peak,
|
|
edge,
|
|
edge,
|
|
peak);
|
|
}
|
|
}
|