Skip to content

Commit

Permalink
Look to add ball bouncing animation
Browse files Browse the repository at this point in the history
  • Loading branch information
tznind committed Jul 6, 2024
1 parent 716ac31 commit 570d5b1
Showing 1 changed file with 399 additions and 0 deletions.
399 changes: 399 additions & 0 deletions Terminal.Gui/TextEffects/Animation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,399 @@
using System;
using System.Collections.Generic;
using static System.Runtime.InteropServices.JavaScript.JSType;
using Terminal.Gui;

public enum SyncMetric
{
Distance,
Step
}

public class CharacterVisual
{
public string Symbol { get; set; }
public bool Bold { get; set; }
public bool Dim { get; set; }
public bool Italic { get; set; }
public bool Underline { get; set; }
public bool Blink { get; set; }
public bool Reverse { get; set; }
public bool Hidden { get; set; }
public bool Strike { get; set; }
public Color Color { get; set; }
public string FormattedSymbol { get; private set; }
private string _colorCode;

public CharacterVisual (string symbol, bool bold = false, bool dim = false, bool italic = false, bool underline = false, bool blink = false, bool reverse = false, bool hidden = false, bool strike = false, Color color = null, string colorCode = null)
{
Symbol = symbol;
Bold = bold;
Dim = dim;
Italic = italic;
Underline = underline;
Blink = blink;
Reverse = reverse;
Hidden = hidden;
Strike = strike;
Color = color;
_colorCode = colorCode;
FormattedSymbol = FormatSymbol ();
}

private string FormatSymbol ()
{
string formattingString = "";
if (Bold) formattingString += Ansitools.ApplyBold ();
if (Italic) formattingString += Ansitools.ApplyItalic ();
if (Underline) formattingString += Ansitools.ApplyUnderline ();
if (Blink) formattingString += Ansitools.ApplyBlink ();
if (Reverse) formattingString += Ansitools.ApplyReverse ();
if (Hidden) formattingString += Ansitools.ApplyHidden ();
if (Strike) formattingString += Ansitools.ApplyStrikethrough ();
if (_colorCode != null) formattingString += Colorterm.Fg (_colorCode);

return $"{formattingString}{Symbol}{(formattingString != "" ? Ansitools.ResetAll () : "")}";
}

public void DisableModes ()
{
Bold = false;
Dim = false;
Italic = false;
Underline = false;
Blink = false;
Reverse = false;
Hidden = false;
Strike = false;
}
}

public class Frame
{
public CharacterVisual CharacterVisual { get; }
public int Duration { get; }
public int TicksElapsed { get; set; }

public Frame (CharacterVisual characterVisual, int duration)
{
CharacterVisual = characterVisual;
Duration = duration;
TicksElapsed = 0;
}

public void IncrementTicks ()
{
TicksElapsed++;
}
}

public class Scene
{
public string SceneId { get; }
public bool IsLooping { get; }
public SyncMetric? Sync { get; }
public EasingFunction Ease { get; }
public bool NoColor { get; set; }
public bool UseXtermColors { get; set; }
public List<Frame> Frames { get; } = new List<Frame> ();
public List<Frame> PlayedFrames { get; } = new List<Frame> ();
public Dictionary<int, Frame> FrameIndexMap { get; } = new Dictionary<int, Frame> ();
public int EasingTotalSteps { get; private set; }
public int EasingCurrentStep { get; private set; }
public static Dictionary<string, int> XtermColorMap { get; } = new Dictionary<string, int> ();

public Scene (string sceneId, bool isLooping = false, SyncMetric? sync = null, EasingFunction ease = null, bool noColor = false, bool useXtermColors = false)
{
SceneId = sceneId;
IsLooping = isLooping;
Sync = sync;
Ease = ease;
NoColor = noColor;
UseXtermColors = useXtermColors;
}

public void AddFrame (string symbol, int duration, Color color = null, bool bold = false, bool dim = false, bool italic = false, bool underline = false, bool blink = false, bool reverse = false, bool hidden = false, bool strike = false)
{
string charVisColor = null;
if (color != null)
{
if (NoColor)
{
charVisColor = null;
}
else if (UseXtermColors)
{
if (color.XtermColor != null)
{
charVisColor = color.XtermColor;
}
else if (XtermColorMap.ContainsKey (color.RgbColor))
{
// Build error says Error CS0029 Cannot implicitly convert type 'int' to 'string' Terminal.Gui (net8.0) D:\Repos\TerminalGuiDesigner\gui.cs\Terminal.Gui\TextEffects\Animation.cs 120 Active
charVisColor = XtermColorMap [color.RgbColor].ToString ();
}
else
{
var xtermColor = Hexterm.HexToXterm (color.RgbColor);
XtermColorMap [color.RgbColor] = int.Parse (xtermColor);
charVisColor = xtermColor;
}
}
else
{
charVisColor = color.RgbColor;
}
}

if (duration < 1)
{
throw new ArgumentException ("duration must be greater than 0");
}

var charVis = new CharacterVisual (symbol, bold, dim, italic, underline, blink, reverse, hidden, strike, color, charVisColor);
var frame = new Frame (charVis, duration);
Frames.Add (frame);
for (int i = 0; i < frame.Duration; i++)
{
FrameIndexMap [EasingTotalSteps] = frame;
EasingTotalSteps++;
}
}

public CharacterVisual Activate ()
{
if (Frames.Count > 0)
{
return Frames [0].CharacterVisual;
}
else
{
throw new InvalidOperationException ("Scene has no frames.");
}
}

public CharacterVisual GetNextVisual ()
{
var currentFrame = Frames [0];
var nextVisual = currentFrame.CharacterVisual;
currentFrame.IncrementTicks ();
if (currentFrame.TicksElapsed == currentFrame.Duration)
{
currentFrame.TicksElapsed = 0;
PlayedFrames.Add (Frames [0]);
Frames.RemoveAt (0);
if (IsLooping && Frames.Count == 0)
{
Frames.AddRange (PlayedFrames);
PlayedFrames.Clear ();
}
}
return nextVisual;
}

public void ApplyGradientToSymbols (Gradient gradient, IList<string> symbols, int duration)
{
int lastIndex = 0;
for (int symbolIndex = 0; symbolIndex < symbols.Count; symbolIndex++)
{
var symbol = symbols [symbolIndex];
double symbolProgress = (symbolIndex + 1) / (double)symbols.Count;
int gradientIndex = (int)(symbolProgress * gradient.Spectrum.Count);
foreach (var color in gradient.Spectrum.GetRange (lastIndex, Math.Max (gradientIndex - lastIndex, 1)))
{
AddFrame (symbol, duration, color);
}
lastIndex = gradientIndex;
}
}

public void ResetScene ()
{
foreach (var sequence in Frames)
{
sequence.TicksElapsed = 0;
PlayedFrames.Add (sequence);
}
Frames.Clear ();
Frames.AddRange (PlayedFrames);
PlayedFrames.Clear ();
}

public override bool Equals (object obj)
{
if (obj is Scene other)
{
return SceneId == other.SceneId;
}
return false;
}

public override int GetHashCode ()
{
return SceneId.GetHashCode ();
}
}

public class Animation
{
public Dictionary<string, Scene> Scenes { get; } = new Dictionary<string, Scene> ();
public EffectCharacter Character { get; }
public Scene ActiveScene { get; private set; }
public bool UseXtermColors { get; set; } = false;
public bool NoColor { get; set; } = false;
public Dictionary<string, int> XtermColorMap { get; } = new Dictionary<string, int> ();
public int ActiveSceneCurrentStep { get; private set; } = 0;
public CharacterVisual CurrentCharacterVisual { get; private set; }

public Animation (EffectCharacter character)
{
Character = character;
CurrentCharacterVisual = new CharacterVisual (character.InputSymbol);
}

public Scene NewScene (bool isLooping = false, SyncMetric? sync = null, EasingFunction ease = null, string id = "")
{
if (string.IsNullOrEmpty (id))
{
bool foundUnique = false;
int currentId = Scenes.Count;
while (!foundUnique)
{
id = $"{Scenes.Count}";
if (!Scenes.ContainsKey (id))
{
foundUnique = true;
}
else
{
currentId++;
}
}
}

var newScene = new Scene (id, isLooping, sync, ease);
Scenes [id] = newScene;
newScene.NoColor = NoColor;
newScene.UseXtermColors = UseXtermColors;
return newScene;
}

public Scene QueryScene (string sceneId)
{
if (!Scenes.TryGetValue (sceneId, out var scene))
{
throw new ArgumentException ($"Scene {sceneId} does not exist.");
}
return scene;
}

public bool ActiveSceneIsComplete ()
{
if (ActiveScene == null)
{
return true;
}
return ActiveScene.Frames.Count == 0 && !ActiveScene.IsLooping;
}

public bool SceneExists (string sceneId)
{
return Scenes.ContainsKey (sceneId);
}

public void ActivateScene (string sceneId)
{
ActiveScene = QueryScene (sceneId);
CurrentCharacterVisual = ActiveScene.Activate ();
ActiveSceneCurrentStep = 0;
}

public void IncrementScene ()
{
if (ActiveScene == null || ActiveSceneIsComplete ())
{
return;
}
CurrentCharacterVisual = ActiveScene.GetNextVisual ();
}
}

public class EffectCharacter
{
public string InputSymbol { get; }
public CharacterVisual CharacterVisual { get; set; }
public Animation Animation { get; set; }

public EffectCharacter (string inputSymbol)
{
InputSymbol = inputSymbol;
CharacterVisual = new CharacterVisual (inputSymbol);
Animation = new Animation (this);
}

public void Animate (string sceneId)
{
Animation.ActivateScene (sceneId);
Animation.IncrementScene ();
CharacterVisual = Animation.CurrentCharacterVisual;
}

public void ResetEffects ()
{
CharacterVisual.DisableModes ();
}
}

public class Color
{
public string RgbColor { get; }
public string XtermColor { get; }

public Color (string rgbColor, string xtermColor)
{
RgbColor = rgbColor;
XtermColor = xtermColor;
}
}

public class Gradient
{
public List<Color> Spectrum { get; }

public Gradient (List<Color> spectrum)
{
Spectrum = spectrum;
}
}

public class EasingFunction
{
// Easing functions implementation
}

// Dummy classes for Ansitools, Colorterm, and Hexterm as placeholders
public static class Ansitools
{
public static string ApplyBold () => "\x1b[1m";
public static string ApplyItalic () => "\x1b[3m";
public static string ApplyUnderline () => "\x1b[4m";
public static string ApplyBlink () => "\x1b[5m";
public static string ApplyReverse () => "\x1b[7m";
public static string ApplyHidden () => "\x1b[8m";
public static string ApplyStrikethrough () => "\x1b[9m";
public static string ResetAll () => "\x1b[0m";
}

public static class Colorterm
{
public static string Fg (string colorCode) => $"\x1b[38;5;{colorCode}m";
}

public static class Hexterm
{
public static string HexToXterm (string hex)
{
// Convert hex color to xterm color code (0-255)
return "15"; // Example output
}
}

0 comments on commit 570d5b1

Please sign in to comment.