Last active
May 11, 2023 12:41
-
-
Save JKamsker/6e2778076e7ef9d6cb9589da264f059b to your computer and use it in GitHub Desktop.
Spectre-RainbowSpinner
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| Usage: | |
| var spinner = new SpectreConsoleSpinner() | |
| .WithRenderer(RenderableSpinner.SpinnerRenderer.Colored(Color.Rainbow)); | |
| // Checking logic | |
| table.UpdateCell(currentRow, 0, spinner); | |
| */ | |
| using System; | |
| using System.Collections.Generic; | |
| using System.Linq; | |
| using System.Text; | |
| using System.Threading.Tasks; | |
| namespace ApDBT.Utils.SpectreUtils; | |
| internal class Rainbowizer | |
| { | |
| public static string RainbowizeString(string input) | |
| { | |
| return RainbowColorGenerator.RainbowizeString(input); | |
| } | |
| private class RainbowColorGenerator | |
| { | |
| private int _hue; | |
| public RainbowColorGenerator() | |
| { | |
| _hue = 0; | |
| } | |
| public static (byte r, byte g, byte b, byte a) GetNextColorRGBA(int index, int length) | |
| { | |
| int hue = (int)Math.Round((360.0 * index) / length); | |
| return HsvToARgb(hue, 1, 1); | |
| } | |
| public (byte r, byte g, byte b, byte a) GetNextColorRGBA() | |
| { | |
| var hue = _hue; | |
| _hue = (_hue + 10) % 360; | |
| return HsvToARgb(hue, 1, 1); | |
| } | |
| public System.Drawing.Color GetNextColor() | |
| { | |
| var rgba = HsvToARgb(_hue, 1, 1); | |
| var encoded = BitConverter.ToInt32(new[] { rgba.r, rgba.g, rgba.b, rgba.a }); | |
| var color = System.Drawing.Color.FromArgb(encoded); | |
| _hue = (_hue + 10) % 360; | |
| return color; | |
| } | |
| private static (byte r, byte g, byte b, byte a) HsvToARgb(int hue, double saturation, double value) | |
| { | |
| double chroma = value * saturation; | |
| double huePrime = hue / 60.0; | |
| double x = chroma * (1 - Math.Abs(huePrime % 2 - 1)); | |
| double red = 0, green = 0, blue = 0; | |
| if (huePrime >= 0 && huePrime < 1) | |
| { | |
| red = chroma; | |
| green = x; | |
| } | |
| else if (huePrime >= 1 && huePrime < 2) | |
| { | |
| red = x; | |
| green = chroma; | |
| } | |
| else if (huePrime >= 2 && huePrime < 3) | |
| { | |
| green = chroma; | |
| blue = x; | |
| } | |
| else if (huePrime >= 3 && huePrime < 4) | |
| { | |
| green = x; | |
| blue = chroma; | |
| } | |
| else if (huePrime >= 4 && huePrime < 5) | |
| { | |
| red = x; | |
| blue = chroma; | |
| } | |
| else if (huePrime >= 5 && huePrime < 6) | |
| { | |
| red = chroma; | |
| blue = x; | |
| } | |
| double m = value - chroma; | |
| byte r = (byte)((red + m) * 255); | |
| byte g = (byte)((green + m) * 255); | |
| byte b = (byte)((blue + m) * 255); | |
| return (r, g, b, 255); | |
| } | |
| public static string RainbowizeString(string input) | |
| { | |
| //var output = new List<ColoredOutput>(); | |
| var sb = new StringBuilder(); | |
| //var colorGenerator = new RainbowColorGenerator(); | |
| int index = 0; | |
| foreach (var c in input) | |
| { | |
| var color = RainbowColorGenerator.GetNextColorRGBA(index, input.Length); | |
| var hexColor = $"#{color.r:x2}{color.b:x2}{color.g:x2}"; | |
| sb.Append($"[{hexColor}]"); | |
| AppendEscaped(sb, c); | |
| sb.Append("[/]"); | |
| index++; | |
| } | |
| return sb.ToString(); | |
| } | |
| /* | |
| return text | |
| .ReplaceExact("[", "[[") | |
| .ReplaceExact("]", "]]"); | |
| */ | |
| private static void AppendEscaped(StringBuilder sb, char input) | |
| { | |
| if (input == '[') | |
| { | |
| sb.Append("[["); | |
| } | |
| else if (input == ']') | |
| { | |
| sb.Append("]]"); | |
| } | |
| else | |
| { | |
| sb.Append(input); | |
| } | |
| } | |
| } | |
| } | |
| using Spectre.Console.Rendering; | |
| using Spectre.Console; | |
| namespace ApDBT.Utils.SpectreUtils; | |
| public class RenderableSpinner : IRenderable | |
| { | |
| private readonly string[] _spinnerFrames; | |
| private int _currentFrameIndex; | |
| private Measurement _measurement; | |
| public SpinnerRenderer Renderer = SpinnerRenderer.Default; | |
| public RenderableSpinner(params string[] spinnerFrames) | |
| { | |
| _spinnerFrames = spinnerFrames; | |
| //_minWith = _spinnerFrames.Min(x => x); | |
| _measurement = new | |
| ( | |
| spinnerFrames.Min(x => x.Length), | |
| spinnerFrames.Max(x => x.Length) | |
| ); | |
| } | |
| public Measurement Measure(RenderOptions options, int maxWidth) | |
| { | |
| // The spinner occupies a fixed width | |
| return _measurement; | |
| } | |
| public IEnumerable<Segment> Render(RenderOptions options, int maxWidth) | |
| { | |
| var currentFrame = _spinnerFrames[_currentFrameIndex]; | |
| _currentFrameIndex = (_currentFrameIndex + 1) % _spinnerFrames.Length; | |
| return Render(options, maxWidth, currentFrame); | |
| } | |
| public virtual IEnumerable<Segment> Render(RenderOptions options, int maxWidth, string currentFrame) | |
| { | |
| //var segment = new Segment(currentFrame); | |
| //yield return segment; | |
| return (Renderer ?? SpinnerRenderer.Default).Render(options, maxWidth, currentFrame); | |
| } | |
| public abstract class SpinnerRenderer | |
| { | |
| public abstract IEnumerable<Segment> Render(RenderOptions options, int maxWidth, string currentFrame); | |
| public static SpinnerRenderer Default => new ColoredSpinnerRenderer(null); | |
| public static SpinnerRenderer Rainbow => new RainbowSpinnerRenderer(); | |
| public static SpinnerRenderer Colored(Color color) => new ColoredSpinnerRenderer(color); | |
| private class ColoredSpinnerRenderer : SpinnerRenderer | |
| { | |
| private readonly Color? _color; | |
| public ColoredSpinnerRenderer(Color? color) | |
| { | |
| _color = color; | |
| } | |
| public override IEnumerable<Segment> Render(RenderOptions options, int maxWidth, string currentFrame) | |
| { | |
| var style = Style.Plain; | |
| if (_color != null) | |
| { | |
| style = style.Foreground(_color.Value); | |
| } | |
| var segment = new Segment(currentFrame, style); | |
| yield return segment; | |
| } | |
| } | |
| private class RainbowSpinnerRenderer : SpinnerRenderer | |
| { | |
| public override IEnumerable<Segment> Render(RenderOptions options, int maxWidth, string currentFrame) | |
| { | |
| var colored = Rainbowizer.RainbowizeString(currentFrame); | |
| return ((IRenderable)new Markup(colored)).Render(options, maxWidth); | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment