From 337d42fd6e44bf46dfb9f36a3c8d15b904a998b8 Mon Sep 17 00:00:00 2001 From: Asriel Camora Date: Fri, 23 Feb 2024 01:20:46 -0800 Subject: [PATCH] Better solver progress colors & add recipenote solver progress --- Craftimizer/Utils/Colors.cs | 22 ++++++++++++++++ Craftimizer/Windows/MacroEditor.cs | 7 +++-- Craftimizer/Windows/RecipeNote.cs | 41 +++++++++++++++++++++++++----- Craftimizer/Windows/SynthHelper.cs | 7 +++-- Solver/Solver.cs | 33 ++++++++++++++++++------ 5 files changed, 92 insertions(+), 18 deletions(-) diff --git a/Craftimizer/Utils/Colors.cs b/Craftimizer/Utils/Colors.cs index 20ade66..c74bdaf 100644 --- a/Craftimizer/Utils/Colors.cs +++ b/Craftimizer/Utils/Colors.cs @@ -1,3 +1,5 @@ +using Dalamud.Interface.Colors; +using ImGuiNET; using System.Numerics; namespace Craftimizer.Utils; @@ -9,4 +11,24 @@ public static class Colors public static readonly Vector4 Durability = new(0.13f, 0.52f, 0.93f, 1f); public static readonly Vector4 HQ = new(0.592f, 0.863f, 0.376f, 1f); public static readonly Vector4 CP = new(0.63f, 0.37f, 0.75f, 1f); + + private static Vector4 SolverProgressBg => ImGui.ColorConvertU32ToFloat4(ImGui.GetColorU32(ImGuiCol.TableBorderLight)); + private static Vector4 SolverProgressFgBland => ImGuiColors.DalamudWhite2; + private static readonly Vector4[] SolverProgressFg = new Vector4[] + { + new(0.87f, 0.19f, 0.30f, 1f), + new(0.96f, 0.62f, 0.12f, 1f), + new(0.97f, 0.84f, 0.00f, 1f), + new(0.37f, 0.69f, 0.35f, 1f), + new(0.21f, 0.30f, 0.98f, 1f), + new(0.26f, 0.62f, 0.94f, 1f), + new(0.70f, 0.49f, 0.88f, 1f), + }; + + public static (Vector4 Background, Vector4 Foreground) GetSolverProgressColors(int? stageValue) => + stageValue is not { } stage ? + (SolverProgressBg, SolverProgressFgBland) : + stage == 0 ? + (SolverProgressBg, SolverProgressFg[0]) : + (SolverProgressFg[(stage - 1) % SolverProgressFg.Length], SolverProgressFg[stage % SolverProgressFg.Length]); } diff --git a/Craftimizer/Windows/MacroEditor.cs b/Craftimizer/Windows/MacroEditor.cs index 395536f..2a95be5 100644 --- a/Craftimizer/Windows/MacroEditor.cs +++ b/Craftimizer/Windows/MacroEditor.cs @@ -1257,8 +1257,11 @@ public sealed class MacroEditor : Window, IDisposable var percentWidth = ImGui.CalcTextSize("100%").X; var progressWidth = availSpace - percentWidth - spacing; var fraction = Math.Clamp((float)solver.ProgressValue / solver.ProgressMax, 0, 1); - using (var color = ImRaii.PushColor(ImGuiCol.PlotHistogram, ImGuiColors.DalamudGrey3)) - ImGui.ProgressBar(fraction, new(progressWidth, ImGui.GetFrameHeight()), string.Empty); + var progressColors = Colors.GetSolverProgressColors(solver.ProgressStage); + + using (ImRaii.PushColor(ImGuiCol.FrameBg, progressColors.Background)) + using (ImRaii.PushColor(ImGuiCol.PlotHistogram, progressColors.Foreground)) + ImGui.ProgressBar(fraction, new(progressWidth, ImGui.GetFrameHeight()), string.Empty); if (ImGui.IsItemHovered()) ImGuiUtils.Tooltip($"Solver Progress: {solver.ProgressValue} / {solver.ProgressMax}"); ImGui.SameLine(0, spacing); diff --git a/Craftimizer/Windows/RecipeNote.cs b/Craftimizer/Windows/RecipeNote.cs index d7a6e5a..4269e4b 100644 --- a/Craftimizer/Windows/RecipeNote.cs +++ b/Craftimizer/Windows/RecipeNote.cs @@ -66,6 +66,7 @@ public sealed unsafe class RecipeNote : Window, IDisposable private CancellationTokenSource? BestMacroTokenSource { get; set; } private Exception? BestMacroException { get; set; } + private Solver.Solver? BestMacroSolver { get; set; } public (Macro, SimulationState)? BestSavedMacro { get; private set; } public bool HasSavedMacro { get; private set; } public SolverSolution? BestSuggestedMacro { get; private set; } @@ -264,19 +265,19 @@ public sealed unsafe class RecipeNote : Window, IDisposable if (BestSavedMacro is { } savedMacro) { ImGuiUtils.TextCentered(savedMacro.Item1.Name, panelWidth); - DrawMacro((savedMacro.Item1.Actions, savedMacro.Item2), a => { savedMacro.Item1.ActionEnumerable = a; Service.Configuration.Save(); }, stepsPanelWidthOffset, true); + DrawMacro((savedMacro.Item1.Actions, savedMacro.Item2), null, a => { savedMacro.Item1.ActionEnumerable = a; Service.Configuration.Save(); }, stepsPanelWidthOffset, true); } else - DrawMacro(null, null, stepsPanelWidthOffset, true); + DrawMacro(null, null, null, stepsPanelWidthOffset, true); } using (var panel = ImRaii2.GroupPanel("Suggested Macro", panelWidth, out _)) { var stepsPanelWidthOffset = ImGui.GetContentRegionAvail().X - panelWidth; if (BestSuggestedMacro is { } suggestedMacro) - DrawMacro((suggestedMacro.Actions, suggestedMacro.State), null, stepsPanelWidthOffset, false); + DrawMacro((suggestedMacro.Actions, suggestedMacro.State), null, null, stepsPanelWidthOffset, false); else - DrawMacro(null, null, stepsPanelWidthOffset, false); + DrawMacro(null, BestMacroSolver, null, stepsPanelWidthOffset, false); } ImGuiHelpers.ScaledDummy(5); @@ -581,7 +582,7 @@ public sealed unsafe class RecipeNote : Window, IDisposable } } - private void DrawMacro((IReadOnlyList Actions, SimulationState State)? macroValue, Action>? setter, float stepsAvailWidthOffset, bool isSavedMacro) + private void DrawMacro((IReadOnlyList Actions, SimulationState State)? macroValue, Solver.Solver? solver, Action>? setter, float stepsAvailWidthOffset, bool isSavedMacro) { var windowHeight = 2 * ImGui.GetFrameHeightWithSpacing(); @@ -590,7 +591,33 @@ public sealed unsafe class RecipeNote : Window, IDisposable if (isSavedMacro && !HasSavedMacro) ImGuiUtils.TextMiddleNewLine("You have no macros!", new(ImGui.GetContentRegionAvail().X - stepsAvailWidthOffset, windowHeight + 1)); else if (BestMacroException == null) - ImGuiUtils.TextMiddleNewLine("Calculating...", new(ImGui.GetContentRegionAvail().X - stepsAvailWidthOffset, windowHeight + 1)); + { + if (solver != null) + { + var calcTextSize = ImGui.CalcTextSize("Calculating..."); + var spacing = ImGui.GetStyle().ItemSpacing.X; + var fraction = Math.Clamp((float)solver.ProgressValue / solver.ProgressMax, 0, 1); + var progressColors = Colors.GetSolverProgressColors(solver.ProgressStage); + + ImGuiUtils.AlignCentered(windowHeight + spacing + calcTextSize.X, ImGui.GetContentRegionAvail().X - stepsAvailWidthOffset); + + ImGuiUtils.ArcProgress( + fraction, + windowHeight / 2f, + .5f, + ImGui.ColorConvertFloat4ToU32(progressColors.Background), + ImGui.ColorConvertFloat4ToU32(progressColors.Foreground)); + if (ImGui.IsItemHovered()) + ImGuiUtils.Tooltip($"Solver Progress: {solver.ProgressValue} / {solver.ProgressMax}"); + + ImGui.SameLine(0, spacing); + + ImGuiUtils.AlignMiddle(calcTextSize, new(calcTextSize.X, windowHeight)); + ImGui.Text("Calculating..."); + } + else + ImGuiUtils.TextMiddleNewLine("Calculating...", new(ImGui.GetContentRegionAvail().X - stepsAvailWidthOffset, windowHeight + 1)); + } else { ImGui.AlignTextToFramePadding(); @@ -833,6 +860,7 @@ public sealed unsafe class RecipeNote : Window, IDisposable { BestMacroTokenSource?.Cancel(); BestMacroTokenSource = new(); + BestMacroSolver = null; BestMacroException = null; BestSavedMacro = null; HasSavedMacro = false; @@ -900,6 +928,7 @@ public sealed unsafe class RecipeNote : Window, IDisposable var solver = new Solver.Solver(config, state) { Token = token }; solver.OnLog += Log.Debug; + BestMacroSolver = solver; solver.Start(); var solution = solver.GetTask().GetAwaiter().GetResult(); diff --git a/Craftimizer/Windows/SynthHelper.cs b/Craftimizer/Windows/SynthHelper.cs index 1b75688..5e52044 100644 --- a/Craftimizer/Windows/SynthHelper.cs +++ b/Craftimizer/Windows/SynthHelper.cs @@ -387,8 +387,11 @@ public sealed unsafe class SynthHelper : Window, IDisposable var percentWidth = ImGui.CalcTextSize("100%").X; var progressWidth = availSpace - percentWidth - spacing; var fraction = Math.Clamp((float)solver.ProgressValue / solver.ProgressMax, 0, 1); - using (var color = ImRaii.PushColor(ImGuiCol.PlotHistogram, ImGuiColors.DalamudGrey3)) - ImGui.ProgressBar(fraction, new(progressWidth, ImGui.GetFrameHeight()), string.Empty); + var progressColors = Colors.GetSolverProgressColors(solver.ProgressStage); + + using (ImRaii.PushColor(ImGuiCol.FrameBg, progressColors.Background)) + using (ImRaii.PushColor(ImGuiCol.PlotHistogram, progressColors.Foreground)) + ImGui.ProgressBar(fraction, new(progressWidth, ImGui.GetFrameHeight()), string.Empty); if (ImGui.IsItemHovered()) ImGuiUtils.Tooltip($"Solver Progress: {solver.ProgressValue} / {solver.ProgressMax}"); ImGui.SameLine(0, spacing); diff --git a/Solver/Solver.cs b/Solver/Solver.cs index b16ddea..cc43434 100644 --- a/Solver/Solver.cs +++ b/Solver/Solver.cs @@ -22,11 +22,22 @@ public sealed class Solver : IDisposable private int progress; private int maxProgress; + private int progressStage; - // In iterative algorithms, the value can be reset back to 0. + // In iterative algorithms, the value can be reset back to 0 (and progress stage increases by 1) // In other algorithms, the value increases monotonically. public int ProgressValue => progress; + // Maximum ProgressValue value. public int ProgressMax => maxProgress; + // Always increases by 1 when ProgressValue is reset. Set to null if the algorithm is not iterative. + public int? ProgressStage + { + get + { + var stage = progressStage; + return stage == -1 ? null : stage; + } + } public delegate void LogDelegate(string text); public delegate void NewActionDelegate(ActionType action); @@ -43,15 +54,17 @@ public sealed class Solver : IDisposable Config = config; State = state; - SearchFunc = Config.Algorithm switch + (SearchFunc, var hasProgressStage) = ((Func>, bool))(Config.Algorithm switch { - SolverAlgorithm.Oneshot => SearchOneshot, - SolverAlgorithm.OneshotForked => SearchOneshotForked, - SolverAlgorithm.Stepwise => SearchStepwise, - SolverAlgorithm.StepwiseForked => SearchStepwiseForked, - SolverAlgorithm.StepwiseFurcated => SearchStepwiseFurcated, + SolverAlgorithm.Oneshot => (SearchOneshot, false), + SolverAlgorithm.OneshotForked => (SearchOneshotForked, false), + SolverAlgorithm.Stepwise => (SearchStepwise, true), + SolverAlgorithm.StepwiseForked => (SearchStepwiseForked, true), + SolverAlgorithm.StepwiseFurcated => (SearchStepwiseFurcated, true), _ => throw new ArgumentOutOfRangeException(nameof(config), config, $"Invalid algorithm: {config.Algorithm}") - }; + }); + + progressStage = hasProgressStage ? 0 : -1; } public void Start() @@ -116,7 +129,11 @@ public sealed class Solver : IDisposable private void ResetProgress() { + if (!ProgressStage.HasValue) + throw new InvalidOperationException("Progress cannot be reset."); + Interlocked.Exchange(ref progress, 0); + Interlocked.Increment(ref progressStage); } private async Task SearchStepwiseFurcated()