Remove everything except gradient

This commit is contained in:
tznind
2024-07-07 10:18:28 +01:00
parent e80f61b171
commit cbcf4b5186
19 changed files with 65 additions and 2653 deletions

View File

@@ -1,5 +1,5 @@

using Terminal.Gui.TextEffects;
using Terminal.Gui.Drawing;
namespace Terminal.Gui;

View File

@@ -1,59 +1,8 @@
namespace Terminal.Gui.TextEffects;
namespace Terminal.Gui;
using System;
using System.Collections.Generic;
using System.Linq;
public class Color
{
public string RgbColor { get; private set; }
public int? XtermColor { get; private set; }
public Color (string rgbColor)
{
if (!ColorUtils.IsValidHexColor (rgbColor))
throw new ArgumentException ("Invalid RGB hex color format.");
RgbColor = rgbColor.StartsWith ("#") ? rgbColor.Substring (1).ToUpper () : rgbColor.ToUpper ();
XtermColor = ColorUtils.HexToXterm (RgbColor); // Convert RGB to XTerm-256
}
public Color (int xtermColor)
{
if (!ColorUtils.IsValidXtermColor (xtermColor))
throw new ArgumentException ("Invalid XTerm-256 color code.");
XtermColor = xtermColor;
RgbColor = ColorUtils.XtermToHex (xtermColor); // Perform the actual conversion
}
public int R => Convert.ToInt32 (RgbColor.Substring (0, 2), 16);
public int G => Convert.ToInt32 (RgbColor.Substring (2, 2), 16);
public int B => Convert.ToInt32 (RgbColor.Substring (4, 2), 16);
public (int R, int G, int B) GetRgbInts ()
{
return (
Convert.ToInt32 (RgbColor.Substring (0, 2), 16),
Convert.ToInt32 (RgbColor.Substring (2, 2), 16),
Convert.ToInt32 (RgbColor.Substring (4, 2), 16)
);
}
public override string ToString () => $"#{RgbColor}";
public static Color FromRgb (int r, int g, int b)
{
// Validate the RGB values to ensure they are within the 0-255 range
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255)
throw new ArgumentOutOfRangeException ("RGB values must be between 0 and 255.");
// Convert RGB values to a hexadecimal string
string rgbColor = $"#{r:X2}{g:X2}{b:X2}";
// Create and return a new Color instance using the hexadecimal string
return new Color (rgbColor);
}
}
public class Gradient
{
public List<Color> Spectrum { get; private set; }
@@ -127,13 +76,13 @@ public class Gradient
int r = (int)(start.R + fraction * (end.R - start.R));
int g = (int)(start.G + fraction * (end.G - start.G));
int b = (int)(start.B + fraction * (end.B - start.B));
yield return Color.FromRgb (r, g, b);
yield return new Color (r, g, b);
}
}
public Dictionary<Coord, Color> BuildCoordinateColorMapping (int maxRow, int maxColumn, Direction direction)
public Dictionary<Point, Color> BuildCoordinateColorMapping (int maxRow, int maxColumn, Direction direction)
{
var gradientMapping = new Dictionary<Coord, Color> ();
var gradientMapping = new Dictionary<Point, Color> ();
switch (direction)
{
@@ -144,7 +93,7 @@ public class Gradient
Color color = GetColorAtFraction (fraction);
for (int col = 0; col <= maxColumn; col++)
{
gradientMapping [new Coord (col, row)] = color;
gradientMapping [new Point (col, row)] = color;
}
}
break;
@@ -156,7 +105,7 @@ public class Gradient
Color color = GetColorAtFraction (fraction);
for (int row = 0; row <= maxRow; row++)
{
gradientMapping [new Coord (col, row)] = color;
gradientMapping [new Point (col, row)] = color;
}
}
break;
@@ -166,9 +115,9 @@ public class Gradient
{
for (int col = 0; col <= maxColumn; col++)
{
double distanceFromCenter = FindNormalizedDistanceFromCenter (maxRow, maxColumn, new Coord (col, row));
double distanceFromCenter = FindNormalizedDistanceFromCenter (maxRow, maxColumn, new Point (col, row));
Color color = GetColorAtFraction (distanceFromCenter);
gradientMapping [new Coord (col, row)] = color;
gradientMapping [new Point (col, row)] = color;
}
}
break;
@@ -178,9 +127,9 @@ public class Gradient
{
for (int col = 0; col <= maxColumn; col++)
{
double fraction = ((double)row * 2 + col) / ((maxRow * 2) + maxColumn);
double fraction = ((double)row * 2 + col) / (maxRow * 2 + maxColumn);
Color color = GetColorAtFraction (fraction);
gradientMapping [new Coord (col, row)] = color;
gradientMapping [new Point (col, row)] = color;
}
}
break;
@@ -189,12 +138,12 @@ public class Gradient
return gradientMapping;
}
private double FindNormalizedDistanceFromCenter (int maxRow, int maxColumn, Coord coord)
private double FindNormalizedDistanceFromCenter (int maxRow, int maxColumn, Point coord)
{
double centerX = maxColumn / 2.0;
double centerY = maxRow / 2.0;
double dx = coord.Column - centerX;
double dy = coord.Row - centerY;
double dx = coord.X - centerX;
double dy = coord.Y - centerY;
double distance = Math.Sqrt (dx * dx + dy * dy);
double maxDistance = Math.Sqrt (centerX * centerX + centerY * centerY);
return distance / maxDistance;

View File

@@ -0,0 +1,24 @@
namespace Terminal.Gui;
/// <summary>
/// Implementation of <see cref="IFill"/> that uses a color gradient (including
/// radial, diagonal etc).
/// </summary>
public class GradientFill : IFill
{
private Dictionary<Point, Color> _map;
public GradientFill (Rectangle area, Gradient gradient, Gradient.Direction direction)
{
_map = gradient.BuildCoordinateColorMapping (area.Height, area.Width, direction);
}
public Color GetColor (Point point)
{
if (_map.TryGetValue (point, out var color))
{
return color;
}
return new Color (0, 0, 0); // Default to black if point not found
}
}

View File

@@ -1,4 +1,4 @@
namespace Terminal.Gui.TextEffects;
namespace Terminal.Gui.Drawing;
/// <summary>
@@ -6,13 +6,13 @@
/// </summary>
public class SolidFill : IFill
{
readonly Terminal.Gui.Color _color;
readonly Color _color;
public SolidFill (Terminal.Gui.Color color)
public SolidFill (Color color)
{
_color = color;
}
public Gui.Color GetColor (Point point)
public Color GetColor (Point point)
{
return _color;
}

View File

@@ -1,502 +0,0 @@

using static Terminal.Gui.TextEffects.EventHandler;
namespace Terminal.Gui.TextEffects;
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; // Holds the ANSI color code or similar string directly
public string ColorCode => _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; // Initialize _colorCode from the constructor argument
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); // Use the direct color code
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; set; }
public int EasingCurrentStep { get; 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;
EasingTotalSteps = 0;
EasingCurrentStep = 0;
}
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 && color.XtermColor.HasValue)
{
charVisColor = color.XtermColor.Value.ToString ();
}
else if (color.RgbColor != null && XtermColorMap.ContainsKey (color.RgbColor))
{
charVisColor = XtermColorMap [color.RgbColor].ToString ();
}
else
{
charVisColor = color.RgbColor;
}
}
if (duration < 1)
throw new ArgumentException ("Duration must be greater than 0.");
var characterVisual = new CharacterVisual (symbol, bold, dim, italic, underline, blink, reverse, hidden, strike, color, charVisColor);
var frame = new Frame (characterVisual, duration);
Frames.Add (frame);
for (int i = 0; i < frame.Duration; i++)
{
FrameIndexMap [EasingTotalSteps] = frame;
EasingTotalSteps++;
}
}
public CharacterVisual Activate ()
{
if (Frames.Count == 0)
throw new InvalidOperationException ("Scene has no frames.");
EasingCurrentStep = 0;
return Frames [0].CharacterVisual;
}
public CharacterVisual GetNextVisual ()
{
if (Frames.Count == 0)
return null;
var frame = Frames [0];
if (++EasingCurrentStep >= frame.Duration)
{
EasingCurrentStep = 0;
PlayedFrames.Add (frame);
Frames.RemoveAt (0);
if (IsLooping && Frames.Count == 0)
{
Frames.AddRange (PlayedFrames);
PlayedFrames.Clear ();
}
if (Frames.Count > 0)
return Frames [0].CharacterVisual;
}
return frame.CharacterVisual;
}
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 ()
{
EasingCurrentStep = 0;
Frames.Clear ();
Frames.AddRange (PlayedFrames);
PlayedFrames.Clear ();
}
public override bool Equals (object obj)
{
return obj is Scene other && SceneId == other.SceneId;
}
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 void SetAppearance (string symbol, Color? color = null)
{
string charVisColor = null;
if (color != null)
{
if (NoColor)
{
charVisColor = null;
}
else if (UseXtermColors)
{
charVisColor = color.XtermColor.ToString();
}
else
{
charVisColor = color.RgbColor;
}
}
CurrentCharacterVisual = new CharacterVisual (symbol, color: color, colorCode: charVisColor);
}
public static Color RandomColor ()
{
var random = new Random ();
var colorHex = random.Next (0, 0xFFFFFF).ToString ("X6");
return new Color (colorHex);
}
public static Color AdjustColorBrightness (Color color, float brightness)
{
float HueToRgb (float p, float q, float t)
{
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6f) return p + (q - p) * 6 * t;
if (t < 1 / 2f) return q;
if (t < 2 / 3f) return p + (q - p) * (2 / 3f - t) * 6;
return p;
}
float r = int.Parse (color.RgbColor.Substring (0, 2), System.Globalization.NumberStyles.HexNumber) / 255f;
float g = int.Parse (color.RgbColor.Substring (2, 2), System.Globalization.NumberStyles.HexNumber) / 255f;
float b = int.Parse (color.RgbColor.Substring (4, 2), System.Globalization.NumberStyles.HexNumber) / 255f;
float max = Math.Max (r, Math.Max (g, b));
float min = Math.Min (r, Math.Min (g, b));
float h, s, l = (max + min) / 2f;
if (max == min)
{
h = s = 0; // achromatic
}
else
{
float d = max - min;
s = l > 0.5f ? d / (2f - max - min) : d / (max + min);
if (max == r)
{
h = (g - b) / d + (g < b ? 6 : 0);
}
else if (max == g)
{
h = (b - r) / d + 2;
}
else
{
h = (r - g) / d + 4;
}
h /= 6;
}
l = Math.Max (Math.Min (l * brightness, 1), 0);
if (s == 0)
{
r = g = b = l; // achromatic
}
else
{
float q = l < 0.5f ? l * (1 + s) : l + s - l * s;
float p = 2 * l - q;
r = HueToRgb (p, q, h + 1 / 3f);
g = HueToRgb (p, q, h);
b = HueToRgb (p, q, h - 1 / 3f);
}
var adjustedColor = $"{(int)(r * 255):X2}{(int)(g * 255):X2}{(int)(b * 255):X2}";
return new Color (adjustedColor);
}
private float EaseAnimation (EasingFunction easingFunc)
{
if (ActiveScene == null)
{
return 0;
}
float elapsedStepRatio = ActiveScene.EasingCurrentStep / (float)ActiveScene.EasingTotalSteps;
return easingFunc (elapsedStepRatio);
}
public void StepAnimation ()
{
if (ActiveScene != null && ActiveScene.Frames.Count > 0)
{
if (ActiveScene.Sync != null)
{
if (Character.Motion.ActivePath != null)
{
int sequenceIndex = 0;
if (ActiveScene.Sync == SyncMetric.Step)
{
sequenceIndex = (int)Math.Round ((ActiveScene.Frames.Count - 1) *
(Math.Max (Character.Motion.ActivePath.CurrentStep, 1) /
(float)Math.Max (Character.Motion.ActivePath.MaxSteps, 1)));
}
else if (ActiveScene.Sync == SyncMetric.Distance)
{
sequenceIndex = (int)Math.Round ((ActiveScene.Frames.Count - 1) *
(Math.Max (Math.Max (Character.Motion.ActivePath.TotalDistance, 1) -
Math.Max (Character.Motion.ActivePath.TotalDistance -
Character.Motion.ActivePath.LastDistanceReached, 1), 1) /
(float)Math.Max (Character.Motion.ActivePath.TotalDistance, 1)));
}
try
{
CurrentCharacterVisual = ActiveScene.Frames [sequenceIndex].CharacterVisual;
}
catch (IndexOutOfRangeException)
{
CurrentCharacterVisual = ActiveScene.Frames [^1].CharacterVisual;
}
}
else
{
CurrentCharacterVisual = ActiveScene.Frames [^1].CharacterVisual;
ActiveScene.PlayedFrames.AddRange (ActiveScene.Frames);
ActiveScene.Frames.Clear ();
}
}
else if (ActiveScene.Ease != null)
{
float easingFactor = EaseAnimation (ActiveScene.Ease);
int frameIndex = (int)Math.Round (easingFactor * Math.Max (ActiveScene.EasingTotalSteps - 1, 0));
frameIndex = Math.Max (Math.Min (frameIndex, ActiveScene.EasingTotalSteps - 1), 0);
Frame frame = ActiveScene.FrameIndexMap [frameIndex];
CurrentCharacterVisual = frame.CharacterVisual;
ActiveScene.EasingCurrentStep++;
if (ActiveScene.EasingCurrentStep == ActiveScene.EasingTotalSteps)
{
if (ActiveScene.IsLooping)
{
ActiveScene.EasingCurrentStep = 0;
}
else
{
ActiveScene.PlayedFrames.AddRange (ActiveScene.Frames);
ActiveScene.Frames.Clear ();
}
}
}
else
{
CurrentCharacterVisual = ActiveScene.GetNextVisual ();
}
if (ActiveSceneIsComplete ())
{
var completedScene = ActiveScene;
if (!ActiveScene.IsLooping)
{
ActiveScene.ResetScene ();
ActiveScene = null;
}
Character.EventHandler.HandleEvent (Event.SceneComplete, completedScene);
}
}
}
public void ActivateScene (Scene scene)
{
ActiveScene = scene;
ActiveSceneCurrentStep = 0;
CurrentCharacterVisual = ActiveScene.Activate ();
Character.EventHandler.HandleEvent (Event.SceneActivated, scene);
}
public void DeactivateScene (Scene scene)
{
if (ActiveScene == scene)
{
ActiveScene = null;
}
}
}
// 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
}
}

View File

@@ -1,266 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using Terminal.Gui.TextEffects;
using Color = Terminal.Gui.TextEffects.Color;
public static class PositiveInt
{
public static int Parse (string arg)
{
if (int.TryParse (arg, out int value) && value > 0)
{
return value;
}
else
{
throw new ArgumentException ($"invalid value: '{arg}' is not > 0.");
}
}
}
public static class NonNegativeInt
{
public static int Parse (string arg)
{
if (int.TryParse (arg, out int value) && value >= 0)
{
return value;
}
else
{
throw new ArgumentException ($"invalid value: '{arg}' Argument must be int >= 0.");
}
}
}
public static class IntRange
{
public static (int, int) Parse (string arg)
{
var parts = arg.Split ('-');
if (parts.Length == 2 && int.TryParse (parts [0], out int start) && int.TryParse (parts [1], out int end) && start > 0 && start <= end)
{
return (start, end);
}
else
{
throw new ArgumentException ($"invalid range: '{arg}' is not a valid range. Must be start-end. Ex: 1-10");
}
}
}
public static class PositiveFloat
{
public static float Parse (string arg)
{
if (float.TryParse (arg, out float value) && value > 0)
{
return value;
}
else
{
throw new ArgumentException ($"invalid value: '{arg}' is not a valid value. Argument must be a float > 0.");
}
}
}
public static class NonNegativeFloat
{
public static float Parse (string arg)
{
if (float.TryParse (arg, out float value) && value >= 0)
{
return value;
}
else
{
throw new ArgumentException ($"invalid argument value: '{arg}' is out of range. Must be float >= 0.");
}
}
}
public static class PositiveFloatRange
{
public static (float, float) Parse (string arg)
{
var parts = arg.Split ('-');
if (parts.Length == 2 && float.TryParse (parts [0], out float start) && float.TryParse (parts [1], out float end) && start > 0 && start <= end)
{
return (start, end);
}
else
{
throw new ArgumentException ($"invalid range: '{arg}' is not a valid range. Must be start-end. Ex: 0.1-1.0");
}
}
}
public static class Ratio
{
public static float Parse (string arg)
{
if (float.TryParse (arg, out float value) && value >= 0 && value <= 1)
{
return value;
}
else
{
throw new ArgumentException ($"invalid value: '{arg}' is not a float >= 0 and <= 1. Example: 0.5");
}
}
}
public static class GradientDirectionParser
{
public static Gradient.Direction Parse (string arg)
{
return arg.ToLower () switch
{
"horizontal" => Gradient.Direction.Horizontal,
"vertical" => Gradient.Direction.Vertical,
"diagonal" => Gradient.Direction.Diagonal,
"radial" => Gradient.Direction.Radial,
_ => throw new ArgumentException ($"invalid gradient direction: '{arg}' is not a valid gradient direction. Choices are diagonal, horizontal, vertical, or radial."),
};
}
}
public static class ColorArg
{
public static Color Parse (string arg)
{
if (int.TryParse (arg, out int xtermValue) && xtermValue >= 0 && xtermValue <= 255)
{
return new Color (xtermValue);
}
else if (arg.Length == 6 && int.TryParse (arg, NumberStyles.HexNumber, null, out int _))
{
return new Color (arg);
}
else
{
throw new ArgumentException ($"invalid color value: '{arg}' is not a valid XTerm or RGB color. Must be in range 0-255 or 000000-FFFFFF.");
}
}
}
public static class Symbol
{
public static string Parse (string arg)
{
if (arg.Length == 1 && IsAsciiOrUtf8 (arg))
{
return arg;
}
else
{
throw new ArgumentException ($"invalid symbol: '{arg}' is not a valid symbol. Must be a single ASCII/UTF-8 character.");
}
}
private static bool IsAsciiOrUtf8 (string s)
{
try
{
Encoding.ASCII.GetBytes (s);
}
catch (EncoderFallbackException)
{
try
{
Encoding.UTF8.GetBytes (s);
}
catch (EncoderFallbackException)
{
return false;
}
}
return true;
}
}
public static class CanvasDimension
{
public static int Parse (string arg)
{
if (int.TryParse (arg, out int value) && value >= -1)
{
return value;
}
else
{
throw new ArgumentException ($"invalid value: '{arg}' is not >= -1.");
}
}
}
public static class TerminalDimensions
{
public static (int, int) Parse (string arg)
{
var parts = arg.Split (' ');
if (parts.Length == 2 && int.TryParse (parts [0], out int width) && int.TryParse (parts [1], out int height) && width >= 0 && height >= 0)
{
return (width, height);
}
else
{
throw new ArgumentException ($"invalid terminal dimensions: '{arg}' is not a valid terminal dimension. Must be >= 0.");
}
}
}
public static class Ease
{
private static readonly Dictionary<string, EasingFunction> easingFuncMap = new ()
{
{"linear", Easing.Linear},
{"in_sine", Easing.InSine},
{"out_sine", Easing.OutSine},
{"in_out_sine", Easing.InOutSine},
{"in_quad", Easing.InQuad},
{"out_quad", Easing.OutQuad},
{"in_out_quad", Easing.InOutQuad},
{"in_cubic", Easing.InCubic},
{"out_cubic", Easing.OutCubic},
{"in_out_cubic", Easing.InOutCubic},
{"in_quart", Easing.InQuart},
{"out_quart", Easing.OutQuart},
{"in_out_quart", Easing.InOutQuart},
{"in_quint", Easing.InQuint},
{"out_quint", Easing.OutQuint},
{"in_out_quint", Easing.InOutQuint},
{"in_expo", Easing.InExpo},
{"out_expo", Easing.OutExpo},
{"in_out_expo", Easing.InOutExpo},
{"in_circ", Easing.InCirc},
{"out_circ", Easing.OutCirc},
{"in_out_circ", Easing.InOutCirc},
{"in_back", Easing.InBack},
{"out_back", Easing.OutBack},
{"in_out_back", Easing.InOutBack},
{"in_elastic", Easing.InElastic},
{"out_elastic", Easing.OutElastic},
{"in_out_elastic", Easing.InOutElastic},
{"in_bounce", Easing.InBounce},
{"out_bounce", Easing.OutBounce},
{"in_out_bounce", Easing.InOutBounce},
};
public static EasingFunction Parse (string arg)
{
if (easingFuncMap.TryGetValue (arg.ToLower (), out var easingFunc))
{
return easingFunc;
}
else
{
throw new ArgumentException ($"invalid ease value: '{arg}' is not a valid ease.");
}
}
}

View File

@@ -1,109 +0,0 @@
namespace Terminal.Gui.TextEffects;
public class EffectCharacter
{
public int CharacterId { get; }
public string InputSymbol { get; }
public Coord InputCoord { get; }
public bool IsVisible { get; set; }
public Animation Animation { get; }
public Motion Motion { get; }
public EventHandler EventHandler { get; }
public int Layer { get; set; }
public bool IsFillCharacter { get; set; }
public EffectCharacter (int characterId, string symbol, int inputColumn, int inputRow)
{
CharacterId = characterId;
InputSymbol = symbol;
InputCoord = new Coord (inputColumn, inputRow);
IsVisible = false;
Animation = new Animation (this);
Motion = new Motion (this);
EventHandler = new EventHandler (this);
Layer = 0;
IsFillCharacter = false;
}
public bool IsActive => !Animation.ActiveSceneIsComplete() || !Motion.MovementIsComplete ();
public void Tick ()
{
Motion.Move ();
Animation.StepAnimation ();
}
}
public class EventHandler
{
public EffectCharacter Character { get; }
public Dictionary<(Event, object), List<(Action, object)>> RegisteredEvents { get; }
public EventHandler (EffectCharacter character)
{
Character = character;
RegisteredEvents = new Dictionary<(Event, object), List<(Action, object)>> ();
}
public void RegisterEvent (Event @event, object caller, Action action, object target)
{
var key = (@event, caller);
if (!RegisteredEvents.ContainsKey (key))
RegisteredEvents [key] = new List<(Action, object)> ();
RegisteredEvents [key].Add ((action, target));
}
public void HandleEvent (Event @event, object caller)
{
var key = (@event, caller);
if (!RegisteredEvents.ContainsKey (key))
return;
foreach (var (action, target) in RegisteredEvents [key])
{
switch (action)
{
case Action.ActivatePath:
Character.Motion.ActivatePath (target as Path);
break;
case Action.DeactivatePath:
Character.Motion.DeactivatePath (target as Path);
break;
case Action.SetLayer:
Character.Layer = (int)target;
break;
case Action.SetCoordinate:
Character.Motion.CurrentCoord = (Coord)target;
break;
case Action.Callback:
// TODO:
throw new NotImplementedException ("TODO, port (target as Action)?.Invoke ()");
break;
default:
throw new ArgumentOutOfRangeException (nameof (action), "Unhandled action.");
}
}
}
public enum Event
{
SegmentEntered,
SegmentExited,
PathActivated,
PathComplete,
PathHolding,
SceneActivated,
SceneComplete
}
public enum Action
{
ActivatePath,
DeactivatePath,
SetLayer,
SetCoordinate,
Callback
}
}

View File

@@ -1,60 +0,0 @@
namespace Terminal.Gui.TextEffects;
public abstract class BaseEffectIterator<T> where T : EffectConfig, new()
{
protected T Config { get; set; }
protected TerminalA Terminal { get; set; }
protected List<EffectCharacter> ActiveCharacters { get; set; } = new List<EffectCharacter> ();
protected BaseEffect<T> Effect { get; }
public BaseEffectIterator (BaseEffect<T> effect)
{
Effect = effect;
Config = effect.EffectConfig;
Terminal = new TerminalA (effect.InputData, effect.TerminalConfig);
}
public void Update ()
{
foreach (var character in ActiveCharacters)
{
character.Tick ();
}
ActiveCharacters.RemoveAll (character => !character.IsActive);
}
}
public abstract class BaseEffect<T> where T : EffectConfig, new()
{
public string InputData { get; set; }
public T EffectConfig { get; set; }
public TerminalConfig TerminalConfig { get; set; }
protected BaseEffect (string inputData)
{
InputData = inputData;
EffectConfig = new T ();
TerminalConfig = new TerminalConfig ();
}
/*
public IDisposable TerminalOutput (string endSymbol = "\n")
{
var terminal = new Terminal (InputData, TerminalConfig);
terminal.PrepCanvas ();
try
{
return terminal;
}
finally
{
terminal.RestoreCursor (endSymbol);
}
}*/
}

View File

@@ -1,303 +0,0 @@
namespace Terminal.Gui.TextEffects;
using System;
public delegate float EasingFunction (float progressRatio);
public static class Easing
{
public static float Linear (float progressRatio)
{
return progressRatio;
}
public static float InSine (float progressRatio)
{
return 1 - (float)Math.Cos ((progressRatio * Math.PI) / 2);
}
public static float OutSine (float progressRatio)
{
return (float)Math.Sin ((progressRatio * Math.PI) / 2);
}
public static float InOutSine (float progressRatio)
{
return -(float)(Math.Cos (Math.PI * progressRatio) - 1) / 2;
}
public static float InQuad (float progressRatio)
{
return progressRatio * progressRatio;
}
public static float OutQuad (float progressRatio)
{
return 1 - (1 - progressRatio) * (1 - progressRatio);
}
public static float InOutQuad (float progressRatio)
{
if (progressRatio < 0.5)
{
return 2 * progressRatio * progressRatio;
}
else
{
return 1 - (float)Math.Pow (-2 * progressRatio + 2, 2) / 2;
}
}
public static float InCubic (float progressRatio)
{
return progressRatio * progressRatio * progressRatio;
}
public static float OutCubic (float progressRatio)
{
return 1 - (float)Math.Pow (1 - progressRatio, 3);
}
public static float InOutCubic (float progressRatio)
{
if (progressRatio < 0.5)
{
return 4 * progressRatio * progressRatio * progressRatio;
}
else
{
return 1 - (float)Math.Pow (-2 * progressRatio + 2, 3) / 2;
}
}
public static float InQuart (float progressRatio)
{
return progressRatio * progressRatio * progressRatio * progressRatio;
}
public static float OutQuart (float progressRatio)
{
return 1 - (float)Math.Pow (1 - progressRatio, 4);
}
public static float InOutQuart (float progressRatio)
{
if (progressRatio < 0.5)
{
return 8 * progressRatio * progressRatio * progressRatio * progressRatio;
}
else
{
return 1 - (float)Math.Pow (-2 * progressRatio + 2, 4) / 2;
}
}
public static float InQuint (float progressRatio)
{
return progressRatio * progressRatio * progressRatio * progressRatio * progressRatio;
}
public static float OutQuint (float progressRatio)
{
return 1 - (float)Math.Pow (1 - progressRatio, 5);
}
public static float InOutQuint (float progressRatio)
{
if (progressRatio < 0.5)
{
return 16 * progressRatio * progressRatio * progressRatio * progressRatio * progressRatio;
}
else
{
return 1 - (float)Math.Pow (-2 * progressRatio + 2, 5) / 2;
}
}
public static float InExpo (float progressRatio)
{
if (progressRatio == 0)
{
return 0;
}
else
{
return (float)Math.Pow (2, 10 * progressRatio - 10);
}
}
public static float OutExpo (float progressRatio)
{
if (progressRatio == 1)
{
return 1;
}
else
{
return 1 - (float)Math.Pow (2, -10 * progressRatio);
}
}
public static float InOutExpo (float progressRatio)
{
if (progressRatio == 0)
{
return 0;
}
else if (progressRatio == 1)
{
return 1;
}
else if (progressRatio < 0.5)
{
return (float)Math.Pow (2, 20 * progressRatio - 10) / 2;
}
else
{
return (2 - (float)Math.Pow (2, -20 * progressRatio + 10)) / 2;
}
}
public static float InCirc (float progressRatio)
{
return 1 - (float)Math.Sqrt (1 - progressRatio * progressRatio);
}
public static float OutCirc (float progressRatio)
{
return (float)Math.Sqrt (1 - (progressRatio - 1) * (progressRatio - 1));
}
public static float InOutCirc (float progressRatio)
{
if (progressRatio < 0.5)
{
return (1 - (float)Math.Sqrt (1 - (2 * progressRatio) * (2 * progressRatio))) / 2;
}
else
{
return ((float)Math.Sqrt (1 - (-2 * progressRatio + 2) * (-2 * progressRatio + 2)) + 1) / 2;
}
}
public static float InBack (float progressRatio)
{
const float c1 = 1.70158f;
const float c3 = c1 + 1;
return c3 * progressRatio * progressRatio * progressRatio - c1 * progressRatio * progressRatio;
}
public static float OutBack (float progressRatio)
{
const float c1 = 1.70158f;
const float c3 = c1 + 1;
return 1 + c3 * (progressRatio - 1) * (progressRatio - 1) * (progressRatio - 1) + c1 * (progressRatio - 1) * (progressRatio - 1);
}
public static float InOutBack (float progressRatio)
{
const float c1 = 1.70158f;
const float c2 = c1 * 1.525f;
if (progressRatio < 0.5)
{
return ((2 * progressRatio) * (2 * progressRatio) * ((c2 + 1) * 2 * progressRatio - c2)) / 2;
}
else
{
return ((2 * progressRatio - 2) * (2 * progressRatio - 2) * ((c2 + 1) * (progressRatio * 2 - 2) + c2) + 2) / 2;
}
}
public static float InElastic (float progressRatio)
{
const float c4 = (2 * (float)Math.PI) / 3;
if (progressRatio == 0)
{
return 0;
}
else if (progressRatio == 1)
{
return 1;
}
else
{
return -(float)Math.Pow (2, 10 * progressRatio - 10) * (float)Math.Sin ((progressRatio * 10 - 10.75) * c4);
}
}
public static float OutElastic (float progressRatio)
{
const float c4 = (2 * (float)Math.PI) / 3;
if (progressRatio == 0)
{
return 0;
}
else if (progressRatio == 1)
{
return 1;
}
else
{
return (float)Math.Pow (2, -10 * progressRatio) * (float)Math.Sin ((progressRatio * 10 - 0.75) * c4) + 1;
}
}
public static float InOutElastic (float progressRatio)
{
const float c5 = (2 * (float)Math.PI) / 4.5f;
if (progressRatio == 0)
{
return 0;
}
else if (progressRatio == 1)
{
return 1;
}
else if (progressRatio < 0.5)
{
return -(float)Math.Pow (2, 20 * progressRatio - 10) * (float)Math.Sin ((20 * progressRatio - 11.125) * c5) / 2;
}
else
{
return ((float)Math.Pow (2, -20 * progressRatio + 10) * (float)Math.Sin ((20 * progressRatio - 11.125) * c5)) / 2 + 1;
}
}
public static float InBounce (float progressRatio)
{
return 1 - OutBounce (1 - progressRatio);
}
public static float OutBounce (float progressRatio)
{
const float n1 = 7.5625f;
const float d1 = 2.75f;
if (progressRatio < 1 / d1)
{
return n1 * progressRatio * progressRatio;
}
else if (progressRatio < 2 / d1)
{
return n1 * (progressRatio - 1.5f / d1) * (progressRatio - 1.5f / d1) + 0.75f;
}
else if (progressRatio < 2.5 / d1)
{
return n1 * (progressRatio - 2.25f / d1) * (progressRatio - 2.25f / d1) + 0.9375f;
}
else
{
return n1 * (progressRatio - 2.625f / d1) * (progressRatio - 2.625f / d1) + 0.984375f;
}
}
public static float InOutBounce (float progressRatio)
{
if (progressRatio < 0.5)
{
return (1 - OutBounce (1 - 2 * progressRatio)) / 2;
}
else
{
return (1 + OutBounce (2 * progressRatio - 1)) / 2;
}
}
}

View File

@@ -1,13 +0,0 @@
namespace Terminal.Gui.TextEffects;
public class EffectConfig
{
public Color ColorSingle { get; set; }
public List<Color> ColorList { get; set; }
public Color FinalColor { get; set; }
public List<Color> FinalGradientStops { get; set; }
public List<int> FinalGradientSteps { get; set; }
public int FinalGradientFrames { get; set; }
public float MovementSpeed { get; set; }
public EasingFunction Easing { get; set; }
}

View File

@@ -1,241 +0,0 @@
/*namespace Terminal.Gui.TextEffects.Effects;
public class BeamsConfig : EffectConfig
{
public string [] BeamRowSymbols { get; set; } = { "▂", "▁", "_" };
public string [] BeamColumnSymbols { get; set; } = { "▌", "▍", "▎", "▏" };
public int BeamDelay { get; set; } = 10;
public (int, int) BeamRowSpeedRange { get; set; } = (10, 40);
public (int, int) BeamColumnSpeedRange { get; set; } = (6, 10);
public Color [] BeamGradientStops { get; set; } = { new Color ("ffffff"), new Color ("00D1FF"), new Color ("8A008A") };
public int [] BeamGradientSteps { get; set; } = { 2, 8 };
public int BeamGradientFrames { get; set; } = 2;
public Color [] FinalGradientStops { get; set; } = { new Color ("8A008A"), new Color ("00D1FF"), new Color ("ffffff") };
public int [] FinalGradientSteps { get; set; } = { 12 };
public int FinalGradientFrames { get; set; } = 5;
public GradientDirection FinalGradientDirection { get; set; } = GradientDirection.Vertical;
public int FinalWipeSpeed { get; set; } = 1;
}
public class Beams : BaseEffect<BeamsConfig>
{
public Beams (string inputData) : base (inputData)
{
}
protected override BaseEffectIterator<BeamsConfig> CreateIterator ()
{
return new BeamsIterator (this);
}
}
public class BeamsIterator : BaseEffectIterator<BeamsConfig>
{
private class Group
{
public List<EffectCharacter> Characters { get; private set; }
public string Direction { get; private set; }
private Terminal Terminal;
private BeamsConfig Config;
private double Speed;
private float NextCharacterCounter;
private List<EffectCharacter> SortedCharacters;
public Group (List<EffectCharacter> characters, string direction, Terminal terminal, BeamsConfig config)
{
Characters = characters;
Direction = direction;
Terminal = terminal;
Config = config;
Speed = new Random ().Next (config.BeamRowSpeedRange.Item1, config.BeamRowSpeedRange.Item2) * 0.1;
NextCharacterCounter = 0;
SortedCharacters = direction == "row"
? characters.OrderBy (c => c.InputCoord.Column).ToList ()
: characters.OrderBy (c => c.InputCoord.Row).ToList ();
if (new Random ().Next (0, 2) == 0)
{
SortedCharacters.Reverse ();
}
}
public void IncrementNextCharacterCounter ()
{
NextCharacterCounter += (float)Speed;
}
public EffectCharacter GetNextCharacter ()
{
NextCharacterCounter -= 1;
var nextCharacter = SortedCharacters.First ();
SortedCharacters.RemoveAt (0);
if (nextCharacter.Animation.ActiveScene != null)
{
nextCharacter.Animation.ActiveScene.ResetScene ();
return null;
}
Terminal.SetCharacterVisibility (nextCharacter, true);
nextCharacter.Animation.ActivateScene (nextCharacter.Animation.QueryScene ("beam_" + Direction));
return nextCharacter;
}
public bool Complete ()
{
return !SortedCharacters.Any ();
}
}
private List<Group> PendingGroups = new List<Group> ();
private Dictionary<EffectCharacter, Color> CharacterFinalColorMap = new Dictionary<EffectCharacter, Color> ();
private List<Group> ActiveGroups = new List<Group> ();
private int Delay = 0;
private string Phase = "beams";
private List<List<EffectCharacter>> FinalWipeGroups;
public BeamsIterator (Beams effect) : base (effect)
{
Build ();
}
private void Build ()
{
var finalGradient = new Gradient (Effect.Config.FinalGradientStops, Effect.Config.FinalGradientSteps);
var finalGradientMapping = finalGradient.BuildCoordinateColorMapping (
Effect.Terminal.Canvas.Top,
Effect.Terminal.Canvas.Right,
Effect.Config.FinalGradientDirection
);
foreach (var character in Effect.Terminal.GetCharacters (fillChars: true))
{
CharacterFinalColorMap [character] = finalGradientMapping [character.InputCoord];
}
var beamGradient = new Gradient (Effect.Config.BeamGradientStops, Effect.Config.BeamGradientSteps);
var groups = new List<Group> ();
foreach (var row in Effect.Terminal.GetCharactersGrouped (Terminal.CharacterGroup.RowTopToBottom, fillChars: true))
{
groups.Add (new Group (row, "row", Effect.Terminal, Effect.Config));
}
foreach (var column in Effect.Terminal.GetCharactersGrouped (Terminal.CharacterGroup.ColumnLeftToRight, fillChars: true))
{
groups.Add (new Group (column, "column", Effect.Terminal, Effect.Config));
}
foreach (var group in groups)
{
foreach (var character in group.Characters)
{
var beamRowScene = character.Animation.NewScene (id: "beam_row");
var beamColumnScene = character.Animation.NewScene (id: "beam_column");
beamRowScene.ApplyGradientToSymbols (
beamGradient, Effect.Config.BeamRowSymbols, Effect.Config.BeamGradientFrames);
beamColumnScene.ApplyGradientToSymbols (
beamGradient, Effect.Config.BeamColumnSymbols, Effect.Config.BeamGradientFrames);
var fadedColor = character.Animation.AdjustColorBrightness (CharacterFinalColorMap [character], 0.3f);
var fadeGradient = new Gradient (CharacterFinalColorMap [character], fadedColor, steps: 10);
beamRowScene.ApplyGradientToSymbols (fadeGradient, character.InputSymbol, 5);
beamColumnScene.ApplyGradientToSymbols (fadeGradient, character.InputSymbol, 5);
var brightenGradient = new Gradient (fadedColor, CharacterFinalColorMap [character], steps: 10);
var brightenScene = character.Animation.NewScene (id: "brighten");
brightenScene.ApplyGradientToSymbols (
brightenGradient, character.InputSymbol, Effect.Config.FinalGradientFrames);
}
}
PendingGroups = groups;
new Random ().Shuffle (PendingGroups);
}
public override bool MoveNext ()
{
if (Phase != "complete" || ActiveCharacters.Any ())
{
if (Phase == "beams")
{
if (Delay == 0)
{
if (PendingGroups.Any ())
{
for (int i = 0; i < new Random ().Next (1, 6); i++)
{
if (PendingGroups.Any ())
{
ActiveGroups.Add (PendingGroups.First ());
PendingGroups.RemoveAt (0);
}
}
}
Delay = Effect.Config.BeamDelay;
}
else
{
Delay--;
}
foreach (var group in ActiveGroups)
{
group.IncrementNextCharacterCounter ();
if ((int)group.NextCharacterCounter > 1)
{
for (int i = 0; i < (int)group.NextCharacterCounter; i++)
{
if (!group.Complete ())
{
var nextChar = group.GetNextCharacter ();
if (nextChar != null)
{
ActiveCharacters.Add (nextChar);
}
}
}
}
}
ActiveGroups = ActiveGroups.Where (g => !g.Complete ()).ToList ();
if (!PendingGroups.Any () && !ActiveGroups.Any () && !ActiveCharacters.Any ())
{
Phase = "final_wipe";
}
}
else if (Phase == "final_wipe")
{
if (FinalWipeGroups.Any ())
{
for (int i = 0; i < Effect.Config.FinalWipeSpeed; i++)
{
if (!FinalWipeGroups.Any ()) break;
var nextGroup = FinalWipeGroups.First ();
FinalWipeGroups.RemoveAt (0);
foreach (var character in nextGroup)
{
character.Animation.ActivateScene (character.Animation.QueryScene ("brighten"));
Effect.Terminal.SetCharacterVisibility (character, true);
ActiveCharacters.Add (character);
}
}
}
else
{
Phase = "complete";
}
}
Update ();
return true;
}
else
{
return false;
}
}
}
*/

View File

@@ -1,137 +0,0 @@
namespace Terminal.Gui.TextEffects;
public static class GeometryUtils
{
public static List<Coord> FindCoordsOnCircle (Coord origin, int radius, int coordsLimit = 0, bool unique = true)
{
var points = new List<Coord> ();
var seenPoints = new HashSet<Coord> ();
if (coordsLimit == 0)
coordsLimit = (int)Math.Ceiling (2 * Math.PI * radius);
double angleStep = 2 * Math.PI / coordsLimit;
for (int i = 0; i < coordsLimit; i++)
{
double angle = i * angleStep;
int x = (int)(origin.Column + radius * Math.Cos (angle));
int y = (int)(origin.Row + radius * Math.Sin (angle));
var coord = new Coord (x, y);
if (unique && !seenPoints.Contains (coord))
{
points.Add (coord);
seenPoints.Add (coord);
}
else if (!unique)
{
points.Add (coord);
}
}
return points;
}
public static List<Coord> FindCoordsInCircle (Coord center, int diameter)
{
var coordsInEllipse = new List<Coord> ();
int radius = diameter / 2;
for (int x = center.Column - radius; x <= center.Column + radius; x++)
{
for (int y = center.Row - radius; y <= center.Row + radius; y++)
{
if (Math.Pow (x - center.Column, 2) + Math.Pow (y - center.Row, 2) <= Math.Pow (radius, 2))
coordsInEllipse.Add (new Coord (x, y));
}
}
return coordsInEllipse;
}
public static List<Coord> FindCoordsInRect (Coord origin, int distance)
{
var coords = new List<Coord> ();
for (int column = origin.Column - distance; column <= origin.Column + distance; column++)
{
for (int row = origin.Row - distance; row <= origin.Row + distance; row++)
{
coords.Add (new Coord (column, row));
}
}
return coords;
}
public static Coord FindCoordAtDistance (Coord origin, Coord target, double distance)
{
double totalDistance = FindLengthOfLine (origin, target) + distance;
double t = distance / totalDistance;
int nextColumn = (int)((1 - t) * origin.Column + t * target.Column);
int nextRow = (int)((1 - t) * origin.Row + t * target.Row);
return new Coord (nextColumn, nextRow);
}
public static Coord FindCoordOnBezierCurve (Coord start, List<Coord> controlPoints, Coord end, double t)
{
// Implementing De Casteljau's algorithm for Bezier curve
if (controlPoints.Count == 1) // Quadratic
{
double x = Math.Pow (1 - t, 2) * start.Column +
2 * (1 - t) * t * controlPoints [0].Column +
Math.Pow (t, 2) * end.Column;
double y = Math.Pow (1 - t, 2) * start.Row +
2 * (1 - t) * t * controlPoints [0].Row +
Math.Pow (t, 2) * end.Row;
return new Coord ((int)x, (int)y);
}
else if (controlPoints.Count == 2) // Cubic
{
double x = Math.Pow (1 - t, 3) * start.Column +
3 * Math.Pow (1 - t, 2) * t * controlPoints [0].Column +
3 * (1 - t) * Math.Pow (t, 2) * controlPoints [1].Column +
Math.Pow (t, 3) * end.Column;
double y = Math.Pow (1 - t, 3) * start.Row +
3 * Math.Pow (1 - t, 2) * t * controlPoints [0].Row +
3 * (1 - t) * Math.Pow (t, 2) * controlPoints [1].Row +
Math.Pow (t, 3) * end.Row;
return new Coord ((int)x, (int)y);
}
throw new ArgumentException ("Invalid number of control points for bezier curve");
}
public static Coord FindCoordOnLine (Coord start, Coord end, double t)
{
int x = (int)((1 - t) * start.Column + t * end.Column);
int y = (int)((1 - t) * start.Row + t * end.Row);
return new Coord (x, y);
}
public static double FindLengthOfBezierCurve (Coord start, List<Coord> controlPoints, Coord end)
{
double length = 0.0;
Coord prevCoord = start;
for (int i = 1; i <= 10; i++)
{
double t = i / 10.0;
Coord coord = FindCoordOnBezierCurve (start, controlPoints, end, t);
length += FindLengthOfLine (prevCoord, coord);
prevCoord = coord;
}
return length;
}
public static double FindLengthOfLine (Coord coord1, Coord coord2)
{
return Math.Sqrt (Math.Pow (coord2.Column - coord1.Column, 2) +
Math.Pow (coord2.Row - coord1.Row, 2));
}
public static double FindNormalizedDistanceFromCenter (int maxRow, int maxColumn, Coord otherCoord)
{
double center_x = maxColumn / 2.0;
double center_y = maxRow / 2.0;
double maxDistance = Math.Sqrt (Math.Pow (maxColumn, 2) + Math.Pow (maxRow, 2));
double distance = Math.Sqrt (Math.Pow (otherCoord.Column - center_x, 2) +
Math.Pow (otherCoord.Row - center_y, 2));
return distance / (maxDistance / 2);
}
}

View File

@@ -1,94 +0,0 @@
using System.Text.RegularExpressions;
namespace Terminal.Gui.TextEffects;
using System;
using System.Collections.Generic;
using System.Linq;
public static class ColorUtils
{
private static readonly Dictionary<int, string> xtermToHexMap = new Dictionary<int, string>
{
{0, "#000000"}, {1, "#800000"}, {2, "#008000"}, {3, "#808000"}, {4, "#000080"}, {5, "#800080"}, {6, "#008080"}, {7, "#c0c0c0"},
{8, "#808080"}, {9, "#ff0000"}, {10, "#00ff00"}, {11, "#ffff00"}, {12, "#0000ff"}, {13, "#ff00ff"}, {14, "#00ffff"}, {15, "#ffffff"},
{16, "#000000"}, {17, "#00005f"}, {18, "#000087"}, {19, "#0000af"}, {20, "#0000d7"}, {21, "#0000ff"}, {22, "#005f00"}, {23, "#005f5f"},
{24, "#005f87"}, {25, "#005faf"}, {26, "#005fd7"}, {27, "#005fff"}, {28, "#008700"}, {29, "#00875f"}, {30, "#008787"}, {31, "#0087af"},
{32, "#0087d7"}, {33, "#0087ff"}, {34, "#00af00"}, {35, "#00af5f"}, {36, "#00af87"}, {37, "#00afaf"}, {38, "#00afd7"}, {39, "#00afff"},
{40, "#00d700"}, {41, "#00d75f"}, {42, "#00d787"}, {43, "#00d7af"}, {44, "#00d7d7"}, {45, "#00d7ff"}, {46, "#00ff00"}, {47, "#00ff5f"},
{48, "#00ff87"}, {49, "#00ffaf"}, {50, "#00ffd7"}, {51, "#00ffff"}, {52, "#5f0000"}, {53, "#5f005f"}, {54, "#5f0087"}, {55, "#5f00af"},
{56, "#5f00d7"}, {57, "#5f00ff"}, {58, "#5f5f00"}, {59, "#5f5f5f"}, {60, "#5f5f87"}, {61, "#5f5faf"}, {62, "#5f5fd7"}, {63, "#5f5fff"},
{64, "#5f8700"}, {65, "#5f875f"}, {66, "#5f8787"}, {67, "#5f87af"}, {68, "#5f87d7"}, {69, "#5f87ff"}, {70, "#5faf00"}, {71, "#5faf5f"},
{72, "#5faf87"}, {73, "#5fafaf"}, {74, "#5fafd7"}, {75, "#5fafff"}, {76, "#5fd700"}, {77, "#5fd75f"}, {78, "#5fd787"}, {79, "#5fd7af"},
{80, "#5fd7d7"}, {81, "#5fd7ff"}, {82, "#5fff00"}, {83, "#5fff5f"}, {84, "#5fff87"}, {85, "#5fffaf"}, {86, "#5fffd7"}, {87, "#5fffff"},
{88, "#870000"}, {89, "#87005f"}, {90, "#870087"}, {91, "#8700af"}, {92, "#8700d7"}, {93, "#8700ff"}, {94, "#875f00"}, {95, "#875f5f"},
{96, "#875f87"}, {97, "#875faf"}, {98, "#875fd7"}, {99, "#875fff"}, {100, "#878700"}, {101, "#87875f"}, {102, "#878787"}, {103, "#8787af"},
{104, "#8787d7"}, {105, "#8787ff"}, {106, "#87af00"}, {107, "#87af5f"}, {108, "#87af87"}, {109, "#87afaf"}, {110, "#87afd7"}, {111, "#87afff"},
{112, "#87d700"}, {113, "#87d75f"}, {114, "#87d787"}, {115, "#87d7af"}, {116, "#87d7d7"}, {117, "#87d7ff"}, {118, "#87ff00"}, {119, "#87ff5f"},
{120, "#87ff87"}, {121, "#87ffaf"}, {122, "#87ffd7"}, {123, "#87ffff"}, {124, "#af0000"}, {125, "#af005f"}, {126, "#af0087"}, {127, "#af00af"},
{128, "#af00d7"}, {129, "#af00ff"}, {130, "#af5f00"}, {131, "#af5f5f"}, {132, "#af5f87"}, {133, "#af5faf"}, {134, "#af5fd7"}, {135, "#af5fff"},
{136, "#af8700"}, {137, "#af875f"}, {138, "#af8787"}, {139, "#af87af"}, {140, "#af87d7"}, {141, "#af87ff"}, {142, "#afaf00"}, {143, "#afaf5f"},
{144, "#afaf87"}, {145, "#afafaf"}, {146, "#afafd7"}, {147, "#afafff"}, {148, "#afd700"}, {149, "#afd75f"}, {150, "#afd787"}, {151, "#afd7af"},
{152, "#afd7d7"}, {153, "#afd7ff"}, {154, "#afff00"}, {155, "#afff5f"}, {156, "#afff87"}, {157, "#afffaf"}, {158, "#afffd7"}, {159, "#afffff"},
{160, "#d70000"}, {161, "#d7005f"}, {162, "#d70087"}, {163, "#d700af"}, {164, "#d700d7"}, {165, "#d700ff"}, {166, "#d75f00"}, {167, "#d75f5f"},
{168, "#d75f87"}, {169, "#d75faf"}, {170, "#d75fd7"}, {171, "#d75fff"}, {172, "#d78700"}, {173, "#d7875f"}, {174, "#d78787"}, {175, "#d787af"},
{176, "#d787d7"}, {177, "#d787ff"}, {178, "#d7af00"}, {179, "#d7af5f"}, {180, "#d7af87"}, {181, "#d7afaf"}, {182, "#d7afd7"}, {183, "#d7afff"},
{184, "#d7d700"}, {185, "#d7d75f"}, {186, "#d7d787"}, {187, "#d7d7af"}, {188, "#d7d7d7"}, {189, "#d7d7ff"}, {190, "#d7ff00"}, {191, "#d7ff5f"},
{192, "#d7ff87"}, {193, "#d7ffaf"}, {194, "#d7ffd7"}, {195, "#d7ffff"}, {196, "#ff0000"}, {197, "#ff005f"}, {198, "#ff0087"}, {199, "#ff00af"},
{200, "#ff00d7"}, {201, "#ff00ff"}, {202, "#ff5f00"}, {203, "#ff5f5f"}, {204, "#ff5f87"}, {205, "#ff5faf"}, {206, "#ff5fd7"}, {207, "#ff5fff"},
{208, "#ff8700"}, {209, "#ff875f"}, {210, "#ff8787"}, {211, "#ff87af"}, {212, "#ff87d7"}, {213, "#ff87ff"}, {214, "#ffaf00"}, {215, "#ffaf5f"},
{216, "#ffaf87"}, {217, "#ffafaf"}, {218, "#ffafd7"}, {219, "#ffafff"}, {220, "#ffd700"}, {221, "#ffd75f"}, {222, "#ffd787"}, {223, "#ffd7af"},
{224, "#ffd7d7"}, {225, "#ffd7ff"}, {226, "#ffff00"}, {227, "#ffff5f"}, {228, "#ffff87"}, {229, "#ffffaf"}, {230, "#ffffd7"}, {231, "#ffffff"},
{232, "#080808"}, {233, "#121212"}, {234, "#1c1c1c"}, {235, "#262626"}, {236, "#303030"}, {237, "#3a3a3a"}, {238, "#444444"}, {239, "#4e4e4e"},
{240, "#585858"}, {241, "#626262"}, {242, "#6c6c6c"}, {243, "#767676"}, {244, "#808080"}, {245, "#8a8a8a"}, {246, "#949494"}, {247, "#9e9e9e"},
{248, "#a8a8a8"}, {249, "#b2b2b2"}, {250, "#bcbcbc"}, {251, "#c6c6c6"}, {252, "#d0d0d0"}, {253, "#dadada"}, {254, "#e4e4e4"}, {255, "#eeeeee"}
};
private static readonly Dictionary<int, (int R, int G, int B)> xtermToRgbMap = xtermToHexMap.ToDictionary (
item => item.Key,
item => (
R: Convert.ToInt32 (item.Value.Substring (1, 2), 16),
G: Convert.ToInt32 (item.Value.Substring (3, 2), 16),
B: Convert.ToInt32 (item.Value.Substring (5, 2), 16)
));
private static readonly Regex hexColorRegex = new Regex ("^#?[0-9A-Fa-f]{6}$");
public static bool IsValidHexColor (string hexColor)
{
return hexColorRegex.IsMatch (hexColor);
}
public static bool IsValidXtermColor (int xtermColor)
{
return xtermColor >= 0 && xtermColor <= 255;
}
public static string XtermToHex (int xtermColor)
{
if (xtermToHexMap.TryGetValue (xtermColor, out string hex))
{
return hex;
}
throw new ArgumentException ($"Invalid XTerm-256 color code: {xtermColor}");
}
public static int HexToXterm (string hexColor)
{
if (!IsValidHexColor (hexColor))
throw new ArgumentException ("Invalid RGB hex color format.");
hexColor = hexColor.StartsWith ("#") ? hexColor.Substring (1) : hexColor;
var rgb = (
R: Convert.ToInt32 (hexColor.Substring (0, 2), 16),
G: Convert.ToInt32 (hexColor.Substring (2, 2), 16),
B: Convert.ToInt32 (hexColor.Substring (4, 2), 16)
);
return xtermToRgbMap.Aggregate ((current, next) =>
ColorDifference (current.Value, rgb) < ColorDifference (next.Value, rgb) ? current : next).Key;
}
private static double ColorDifference ((int R, int G, int B) c1, (int R, int G, int B) c2)
{
return Math.Sqrt (Math.Pow (c1.R - c2.R, 2) + Math.Pow (c1.G - c2.G, 2) + Math.Pow (c1.B - c2.B, 2));
}
}

View File

@@ -1,253 +0,0 @@
namespace Terminal.Gui.TextEffects;
public class Coord
{
public int Column { get; set; }
public int Row { get; set; }
public Coord (int column, int row)
{
Column = column;
Row = row;
}
public override string ToString () => $"({Column}, {Row})";
public override bool Equals (object obj)
{
if (obj is Coord other)
{
return Column == other.Column && Row == other.Row;
}
return false;
}
public override int GetHashCode ()
{
return HashCode.Combine (Column, Row);
}
public static bool operator == (Coord left, Coord right)
{
if (left is null)
{
return right is null;
}
return left.Equals (right);
}
public static bool operator != (Coord left, Coord right)
{
return !(left == right);
}
}
public class Waypoint
{
public string WaypointId { get; set; }
public Coord Coord { get; set; }
public List<Coord> BezierControl { get; set; }
public Waypoint (string waypointId, Coord coord, List<Coord> bezierControl = null)
{
WaypointId = waypointId;
Coord = coord;
BezierControl = bezierControl ?? new List<Coord> ();
}
}
public class Segment
{
public Waypoint Start { get; private set; }
public Waypoint End { get; private set; }
public double Distance { get; private set; }
public bool EnterEventTriggered { get; set; }
public bool ExitEventTriggered { get; set; }
public Segment (Waypoint start, Waypoint end)
{
Start = start;
End = end;
Distance = CalculateDistance (start, end);
}
private double CalculateDistance (Waypoint start, Waypoint end)
{
// Add bezier control point distance calculation if needed
return Math.Sqrt (Math.Pow (end.Coord.Column - start.Coord.Column, 2) + Math.Pow (end.Coord.Row - start.Coord.Row, 2));
}
public Coord GetCoordOnSegment (double distanceFactor)
{
int column = (int)(Start.Coord.Column + (End.Coord.Column - Start.Coord.Column) * distanceFactor);
int row = (int)(Start.Coord.Row + (End.Coord.Row - Start.Coord.Row) * distanceFactor);
return new Coord (column, row);
}
}
public class Path
{
public string PathId { get; private set; }
public double Speed { get; set; }
public Func<double, double> EaseFunction { get; set; }
public int Layer { get; set; }
public int HoldTime { get; set; }
public bool Loop { get; set; }
public List<Segment> Segments { get; private set; } = new List<Segment> ();
public int CurrentStep { get; set; }
public double TotalDistance { get; set; }
public double LastDistanceReached { get; set; }
public int MaxSteps => (int)Math.Ceiling (TotalDistance / Speed); // Calculates max steps based on total distance and speed
public Path (string pathId, double speed, Func<double, double> easeFunction = null, int layer = 0, int holdTime = 0, bool loop = false)
{
PathId = pathId;
Speed = speed;
EaseFunction = easeFunction;
Layer = layer;
HoldTime = holdTime;
Loop = loop;
}
public void AddWaypoint (Waypoint waypoint)
{
if (Segments.Count > 0)
{
var lastSegment = Segments.Last ();
var newSegment = new Segment (lastSegment.End, waypoint);
Segments.Add (newSegment);
TotalDistance += newSegment.Distance;
}
else
{
var originWaypoint = new Waypoint ("origin", new Coord (0, 0)); // Assuming the path starts at origin
var initialSegment = new Segment (originWaypoint, waypoint);
Segments.Add (initialSegment);
TotalDistance = initialSegment.Distance;
}
}
public Coord Step ()
{
if (CurrentStep <= MaxSteps)
{
double progress = EaseFunction?.Invoke ((double)CurrentStep / TotalDistance) ?? (double)CurrentStep / TotalDistance;
double distanceTravelled = TotalDistance * progress;
LastDistanceReached = distanceTravelled;
foreach (var segment in Segments)
{
if (distanceTravelled <= segment.Distance)
{
double segmentProgress = distanceTravelled / segment.Distance;
return segment.GetCoordOnSegment (segmentProgress);
}
distanceTravelled -= segment.Distance;
}
}
return Segments.Last ().End.Coord; // Return the end of the last segment if out of bounds
}
}
public class Motion
{
public Dictionary<string, Path> Paths { get; private set; } = new Dictionary<string, Path> ();
public Path ActivePath { get; private set; }
public Coord CurrentCoord { get; set; }
public Coord PreviousCoord { get; set; }
public EffectCharacter Character { get; private set; } // Assuming EffectCharacter is similar to base_character.EffectCharacter
public Motion (EffectCharacter character)
{
Character = character;
CurrentCoord = new Coord (character.InputCoord.Column, character.InputCoord.Row); // Assuming similar properties
PreviousCoord = new Coord (-1, -1);
}
public void SetCoordinate (Coord coord)
{
CurrentCoord = coord;
}
public Path CreatePath (string pathId, double speed, Func<double, double> easeFunction = null, int layer = 0, int holdTime = 0, bool loop = false)
{
if (Paths.ContainsKey (pathId))
throw new ArgumentException ($"A path with ID {pathId} already exists.");
var path = new Path (pathId, speed, easeFunction, layer, holdTime, loop);
Paths [pathId] = path;
return path;
}
public Path QueryPath (string pathId)
{
if (!Paths.TryGetValue (pathId, out var path))
throw new KeyNotFoundException ($"No path found with ID {pathId}.");
return path;
}
public bool MovementIsComplete ()
{
return ActivePath == null || ActivePath.CurrentStep >= ActivePath.TotalDistance;
}
public void ActivatePath (Path path)
{
if (path == null)
throw new ArgumentNullException (nameof (path), "Path cannot be null when activating.");
ActivePath = path;
ActivePath.CurrentStep = 0; // Reset the path's progress
}
/// <summary>
/// Set the active path to None if the active path is the given path.
/// </summary>
public void DeactivatePath (Path p)
{
if (p == ActivePath)
{
ActivePath = null;
}
}
public void DeactivatePath ()
{
ActivePath = null;
}
public void Move ()
{
if (ActivePath != null)
{
PreviousCoord = CurrentCoord;
CurrentCoord = ActivePath.Step ();
ActivePath.CurrentStep++;
if (ActivePath.CurrentStep >= ActivePath.TotalDistance)
{
if (ActivePath.Loop)
ActivePath.CurrentStep = 0; // Reset the path for looping
else
DeactivatePath (); // Deactivate the path if it is not set to loop
}
}
}
public void ChainPaths (IEnumerable<Path> paths, bool loop = false)
{
var pathList = paths.ToList ();
for (int i = 0; i < pathList.Count; i++)
{
var currentPath = pathList [i];
var nextPath = i + 1 < pathList.Count ? pathList [i + 1] : pathList.FirstOrDefault ();
// Here we could define an event system to trigger path activation when another completes
// For example, you could listen for a "path complete" event and then activate the next path
if (loop && nextPath != null)
{
// Implementation depends on your event system
}
}
}
}

View File

@@ -1,35 +0,0 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Terminal.Gui.TextEffects;
/// <summary>
/// Implementation of <see cref="IFill"/> that uses a color gradient (including
/// radial, diagonal etc).
/// </summary>
public class GradientFill : IFill
{
private Dictionary<Point, Terminal.Gui.Color> _map;
public GradientFill (Rectangle area, Gradient gradient, Gradient.Direction direction)
{
_map =
gradient.BuildCoordinateColorMapping (area.Height, area.Width, direction)
.ToDictionary (
(k) => new Point (k.Key.Column, k.Key.Row),
(v) => new Terminal.Gui.Color (v.Value.R, v.Value.G, v.Value.B));
}
public Terminal.Gui.Color GetColor (Point point)
{
if (_map.TryGetValue (point, out var color))
{
return color;
}
return new Terminal.Gui.Color (0, 0, 0); // Default to black if point not found
}
}

View File

@@ -1,106 +0,0 @@
namespace Terminal.Gui.TextEffects;
public class TerminalConfig
{
public int TabWidth { get; set; } = 4;
public bool XtermColors { get; set; } = false;
public bool NoColor { get; set; } = false;
public bool WrapText { get; set; } = false;
public float FrameRate { get; set; } = 100.0f;
public int CanvasWidth { get; set; } = -1;
public int CanvasHeight { get; set; } = -1;
public string AnchorCanvas { get; set; } = "sw";
public string AnchorText { get; set; } = "sw";
public bool IgnoreTerminalDimensions { get; set; } = false;
}
public class Canvas
{
public int Top { get; private set; }
public int Right { get; private set; }
public int Bottom { get; private set; } = 1;
public int Left { get; private set; } = 1;
public int CenterRow => (Top + Bottom) / 2;
public int CenterColumn => (Right + Left) / 2;
public Coord Center => new Coord (CenterColumn, CenterRow);
public int Width => Right - Left + 1;
public int Height => Top - Bottom + 1;
public Canvas (int top, int right)
{
Top = top;
Right = right;
}
public bool IsCoordInCanvas (Coord coord)
{
return coord.Column >= Left && coord.Column <= Right &&
coord.Row >= Bottom && coord.Row <= Top;
}
public Coord GetRandomCoord (bool outsideScope = false)
{
var random = new Random ();
if (outsideScope)
{
switch (random.Next (4))
{
case 0: return new Coord (random.Next (Left, Right + 1), Top + 1);
case 1: return new Coord (random.Next (Left, Right + 1), Bottom - 1);
case 2: return new Coord (Left - 1, random.Next (Bottom, Top + 1));
case 3: return new Coord (Right + 1, random.Next (Bottom, Top + 1));
}
}
return new Coord (
random.Next (Left, Right + 1),
random.Next (Bottom, Top + 1));
}
}
public class TerminalA
{
public TerminalConfig Config { get; }
public Canvas Canvas { get; private set; }
private Dictionary<Coord, EffectCharacter> CharacterByInputCoord = new Dictionary<Coord, EffectCharacter> ();
public TerminalA (string input, TerminalConfig config = null)
{
Config = config ?? new TerminalConfig ();
var dimensions = GetTerminalDimensions ();
Canvas = new Canvas (dimensions.height, dimensions.width);
ProcessInput (input);
}
private void ProcessInput (string input)
{
// Handling input processing logic similar to Python's version
}
public string GetPipedInput ()
{
// C# way to get piped input or indicate there's none
return Console.IsInputRedirected ? Console.In.ReadToEnd () : string.Empty;
}
public void Print (string output, bool enforceFrameRate = true)
{
if (enforceFrameRate)
EnforceFrameRate ();
// Move cursor to top and clear the current console line
Console.SetCursorPosition (0, 0);
Console.Write (output);
Console.ResetColor ();
}
private void EnforceFrameRate ()
{
// Limit the printing speed based on the Config.FrameRate
}
private (int width, int height) GetTerminalDimensions ()
{
// Return terminal dimensions or defaults if not determinable
return (Console.WindowWidth, Console.WindowHeight);
}
}

View File

@@ -3,10 +3,8 @@ using System.Collections.Generic;
using System.Text;
using System.Threading;
using Terminal.Gui;
using Terminal.Gui.TextEffects;
using Color = Terminal.Gui.TextEffects.Color;
using Animation = Terminal.Gui.TextEffects.Animation;
using Terminal.Gui.Drawing;
namespace UICatalog.Scenarios;
@@ -68,18 +66,8 @@ public class TextEffectsScenario : Scenario
},
DisplayText = "Gradients"
};
var t2 = new Tab ()
{
View = new BallsView ()
{
Width = Dim.Fill (),
Height = Dim.Fill (),
},
DisplayText = "Ball"
};
tabView.AddTab (t1,false);
tabView.AddTab (t2,false);
w.Add (tabView);
@@ -111,11 +99,11 @@ public class TextEffectsScenario : Scenario
// Define the colors of the gradient stops with more appealing colors
stops = new List<Color>
{
Color.FromRgb(0, 128, 255), // Bright Blue
Color.FromRgb(0, 255, 128), // Bright Green
Color.FromRgb(255, 255, 0), // Bright Yellow
Color.FromRgb(255, 128, 0), // Bright Orange
Color.FromRgb(255, 0, 128) // Bright Pink
new Color(0, 128, 255), // Bright Blue
new Color(0, 255, 128), // Bright Green
new Color(255, 255, 0), // Bright Yellow
new Color(255, 128, 0), // Bright Orange
new Color(255, 0, 128) // Bright Pink
};
// Define the number of steps between each color for smoother transitions
@@ -159,9 +147,9 @@ internal class GradientsView : View
// Define the colors of the gradient stops
var stops = new List<Color>
{
Color.FromRgb(255, 0, 0), // Red
Color.FromRgb(0, 255, 0), // Green
Color.FromRgb(238, 130, 238) // Violet
new Color(255, 0, 0), // Red
new Color(0, 255, 0), // Green
new Color(238, 130, 238) // Violet
};
// Define the number of steps between each color
@@ -182,7 +170,7 @@ internal class GradientsView : View
{
for (int col = 0; col <= maxColumn; col++)
{
var coord = new Coord (col, row);
var coord = new Point (col, row);
var color = gradientMapping [coord];
SetColor (color);
@@ -197,13 +185,13 @@ internal class GradientsView : View
// Define the colors of the rainbow
var stops = new List<Color>
{
Color.FromRgb(255, 0, 0), // Red
Color.FromRgb(255, 165, 0), // Orange
Color.FromRgb(255, 255, 0), // Yellow
Color.FromRgb(0, 128, 0), // Green
Color.FromRgb(0, 0, 255), // Blue
Color.FromRgb(75, 0, 130), // Indigo
Color.FromRgb(238, 130, 238) // Violet
new Color(255, 0, 0), // Red
new Color(255, 165, 0), // Orange
new Color(255, 255, 0), // Yellow
new Color(0, 128, 0), // Green
new Color(0, 0, 255), // Blue
new Color(75, 0, 130), // Indigo
new Color(238, 130, 238) // Violet
};
// Define the number of steps between each color
@@ -240,245 +228,4 @@ internal class GradientsView : View
new Terminal.Gui.Color (color.R, color.G, color.B)
)); // Setting color based on RGB
}
}
internal class BallsView : View
{
private Ball? _ball;
private bool _resized;
private LineCanvas lc;
private Gradient gradient;
protected override void OnViewportChanged (DrawEventArgs e)
{
base.OnViewportChanged (e);
_resized = true;
lc = new LineCanvas (new []{
new StraightLine(new System.Drawing.Point(0,0),10,Orientation.Horizontal,LineStyle.Single),
});
TextEffectsScenario.GetAppealingGradientColors (out var stops, out var steps);
gradient = new Gradient (stops, steps);
var fill = new FillPair (
new GradientFill (new System.Drawing.Rectangle (0, 0, 10, 0), gradient , Gradient.Direction.Horizontal),
new SolidFill(Terminal.Gui.Color.Black)
);
lc.Fill = fill;
}
public override void OnDrawContent (Rectangle viewport)
{
base.OnDrawContent (viewport);
if ((_ball == null && viewport.Width > 0 && viewport.Height > 0) || _resized)
{
_ball = new Ball (this);
_ball.Start ();
_resized = false;
}
_ball?.Draw ();
foreach(var map in lc.GetCellMap())
{
Driver.SetAttribute (map.Value.Value.Attribute.Value);
AddRune (map.Key.X, map.Key.Y, map.Value.Value.Rune);
}
for (int x = 0; x < 10; x++)
{
double fraction = (double)x / 10;
Color color = gradient.GetColorAtFraction (fraction);
SetColor (color);
AddRune (x, 2, new Rune ('█'));
}
var map2 = gradient.BuildCoordinateColorMapping (0,10,Gradient.Direction.Horizontal);
for (int x = 0; x < map2.Count; x++)
{
SetColor (map2[new Coord(x,0)]);
AddRune (x, 3, new Rune ('█'));
}
}
private void SetColor (Color color)
{
// Assuming AddRune is a method you have for drawing at specific positions
Application.Driver.SetAttribute (
new Attribute (
new Terminal.Gui.Color (color.R, color.G, color.B),
new Terminal.Gui.Color (color.R, color.G, color.B)
)); // Setting color based on RGB
}
public class Ball
{
public Animation Animation { get; private set; }
public Scene BouncingScene { get; private set; }
public View Viewport { get; private set; }
public EffectCharacter Character { get; private set; }
public Ball (View viewport)
{
Viewport = viewport;
Character = new EffectCharacter (1, "O", 0, 0);
Animation = Character.Animation;
CreateBouncingScene ();
CreateMotionPath ();
}
private void CreateBouncingScene ()
{
BouncingScene = Animation.NewScene (isLooping: true);
int width = Viewport.Frame.Width;
int height = Viewport.Frame.Height;
double frequency = 4 * Math.PI / width; // Double the frequency
for (int x = 0; x < width; x++)
{
int y = (int)((height - 1) / 2 * (1 + Math.Sin (frequency * x) * 0.8)); // Decrease amplitude
BouncingScene.AddFrame ("O", 1);
}
for (int x = width - 1; x >= 0; x--)
{
int y = (int)((height - 1) / 2 * (1 + Math.Sin (frequency * x) * 0.8)); // Decrease amplitude
BouncingScene.AddFrame ("O", 1);
}
}
private void CreateMotionPath ()
{
int width = Viewport.Frame.Width;
int height = Viewport.Frame.Height;
double frequency = 4 * Math.PI / width; // Double the frequency
var path = Character.Motion.CreatePath ("sineWavePath", speed: 1, loop: true);
for (int x = 0; x < width; x++)
{
int y = (int)((height - 1) / 2 * (1 + Math.Sin (frequency * x) * 0.8)); // Decrease amplitude
path.AddWaypoint (new Waypoint ($"waypoint_{x}", new Coord (x, y)));
}
for (int x = width - 1; x >= 0; x--)
{
int y = (int)((height - 1) / 2 * (1 + Math.Sin (frequency * x) * 0.8)); // Decrease amplitude
path.AddWaypoint (new Waypoint ($"waypoint_{x}", new Coord (x, y)));
}
Character.Motion.ActivatePath (path);
}
public void Start ()
{
Animation.ActivateScene (BouncingScene);
new Thread (() =>
{
while (true)
{
Thread.Sleep (10); // Adjust the speed of animation
Character.Tick ();
Application.Invoke (() => Viewport.SetNeedsDisplay ());
}
})
{ IsBackground = true }.Start ();
}
public void Draw ()
{
Driver.SetAttribute (Viewport.ColorScheme.Normal);
Viewport.AddRune (Character.Motion.CurrentCoord.Column, Character.Motion.CurrentCoord.Row, new Rune ('O'));
}
}
}
public class Ball
{
public Animation Animation { get; private set; }
public Scene BouncingScene { get; private set; }
public View Viewport { get; private set; }
public EffectCharacter Character { get; private set; }
public Ball (View viewport)
{
Viewport = viewport;
Character = new EffectCharacter (1, "O", 0, 0);
Animation = Character.Animation;
CreateBouncingScene ();
CreateMotionPath ();
}
private void CreateBouncingScene ()
{
BouncingScene = Animation.NewScene (isLooping: true);
int width = Viewport.Frame.Width;
int height = Viewport.Frame.Height;
double frequency = 4 * Math.PI / width; // Double the frequency
for (int x = 0; x < width; x++)
{
int y = (int)((height) / 2 * (1 + Math.Sin (frequency * x))); // Decrease amplitude
BouncingScene.AddFrame ("O", 1);
}
for (int x = width - 1; x >= 0; x--)
{
int y = (int)((height) / 2 * (1 + Math.Sin (frequency * x))); // Decrease amplitude
BouncingScene.AddFrame ("O", 1);
}
}
private void CreateMotionPath ()
{
int width = Viewport.Frame.Width;
int height = Viewport.Frame.Height;
double frequency = 4 * Math.PI / width; // Double the frequency
var path = Character.Motion.CreatePath ("sineWavePath", speed: 1, loop: true);
for (int x = 0; x < width; x++)
{
int y = (int)((height) / 2 * (1 + Math.Sin (frequency * x))); // Decrease amplitude
path.AddWaypoint (new Waypoint ($"waypoint_{x}", new Coord (x, y)));
}
for (int x = width - 1; x >= 0; x--)
{
int y = (int)((height) / 2 * (1 + Math.Sin (frequency * x))); // Decrease amplitude
path.AddWaypoint (new Waypoint ($"waypoint_{x}", new Coord (x, y)));
}
Character.Motion.ActivatePath (path);
}
public void Start ()
{
Animation.ActivateScene (BouncingScene);
new Thread (() =>
{
while (true)
{
Thread.Sleep (10); // Adjust the speed of animation
Character.Tick ();
Application.Invoke (() => Viewport.SetNeedsDisplay ());
}
})
{ IsBackground = true }.Start ();
}
public void Draw ()
{
Application.Driver.SetAttribute (Viewport.ColorScheme.Normal);
Viewport.AddRune (Character.Motion.CurrentCoord.Column, Character.Motion.CurrentCoord.Row, new Rune ('O'));
}
}
}

View File

@@ -1,191 +0,0 @@
using Terminal.Gui.TextEffects;
namespace Terminal.Gui.TextEffectsTests;
using Color = Terminal.Gui.TextEffects.Color;
public class AnimationTests
{
private EffectCharacter character;
public AnimationTests ()
{
character = new EffectCharacter (0, "a", 0, 0);
}
[Fact]
public void TestCharacterVisualInit ()
{
var visual = new CharacterVisual (
symbol: "a",
bold: true,
dim: false,
italic: true,
underline: false,
blink: true,
reverse: false,
hidden: true,
strike: false,
color: new Color ("ffffff"),
colorCode: "ffffff"
);
Assert.Equal ("\x1b[1m\x1b[3m\x1b[5m\x1b[8m\x1b[38;2;255;255;255ma\x1b[0m", visual.FormattedSymbol);
Assert.True (visual.Bold);
Assert.False (visual.Dim);
Assert.True (visual.Italic);
Assert.False (visual.Underline);
Assert.True (visual.Blink);
Assert.False (visual.Reverse);
Assert.True (visual.Hidden);
Assert.False (visual.Strike);
Assert.Equal (new Color ("ffffff"), visual.Color);
Assert.Equal ("ffffff", visual.ColorCode);
}
[Fact]
public void TestFrameInit ()
{
var visual = new CharacterVisual (
symbol: "a",
bold: true,
dim: false,
italic: true,
underline: false,
blink: true,
reverse: false,
hidden: true,
strike: false,
color: new Color ("ffffff")
);
var frame = new Frame (characterVisual: visual, duration: 5);
Assert.Equal (visual, frame.CharacterVisual);
Assert.Equal (5, frame.Duration);
Assert.Equal (0, frame.TicksElapsed);
}
[Fact]
public void TestSceneInit ()
{
var scene = new Scene (sceneId: "test_scene", isLooping: true, sync: SyncMetric.Step, ease: Easing.InSine);
Assert.Equal ("test_scene", scene.SceneId);
Assert.True (scene.IsLooping);
Assert.Equal (SyncMetric.Step, scene.Sync);
Assert.Equal (Easing.InSine, scene.Ease);
}
[Fact]
public void TestSceneAddFrame ()
{
var scene = new Scene (sceneId: "test_scene");
scene.AddFrame (symbol: "a", duration: 5, color: new Color ("ffffff"), bold: true, italic: true, blink: true, hidden: true);
Assert.Single (scene.Frames);
var frame = scene.Frames [0];
Assert.Equal ("\x1b[1m\x1b[3m\x1b[5m\x1b[8m\x1b[38;2;255;255;255ma\x1b[0m", frame.CharacterVisual.FormattedSymbol);
Assert.Equal (5, frame.Duration);
Assert.Equal (new Color ("ffffff"), frame.CharacterVisual.Color);
Assert.True (frame.CharacterVisual.Bold);
}
[Fact]
public void TestSceneAddFrameInvalidDuration ()
{
var scene = new Scene (sceneId: "test_scene");
var exception = Assert.Throws<ArgumentException> (() => scene.AddFrame (symbol: "a", duration: 0, color: new Color ("ffffff")));
Assert.Equal ("duration must be greater than 0", exception.Message);
}
[Fact]
public void TestSceneApplyGradientToSymbolsEqualColorsAndSymbols ()
{
var scene = new Scene (sceneId: "test_scene");
var gradient = new Gradient (new [] { new Color ("000000"), new Color ("ffffff") },
steps: new [] { 2 });
var symbols = new List<string> { "a", "b", "c" };
scene.ApplyGradientToSymbols (gradient, symbols, duration: 1);
Assert.Equal (3, scene.Frames.Count);
for (int i = 0; i < scene.Frames.Count; i++)
{
Assert.Equal (1, scene.Frames [i].Duration);
Assert.Equal (gradient.Spectrum [i].RgbColor, scene.Frames [i].CharacterVisual.ColorCode);
}
}
[Fact]
public void TestSceneApplyGradientToSymbolsUnequalColorsAndSymbols ()
{
var scene = new Scene (sceneId: "test_scene");
var gradient = new Gradient (
new [] { new Color ("000000"), new Color ("ffffff") },
steps: new [] { 4 });
var symbols = new List<string> { "q", "z" };
scene.ApplyGradientToSymbols (gradient, symbols, duration: 1);
Assert.Equal (5, scene.Frames.Count);
Assert.Equal (gradient.Spectrum [0].RgbColor, scene.Frames [0].CharacterVisual.ColorCode);
Assert.Contains ("q", scene.Frames [0].CharacterVisual.Symbol);
Assert.Equal (gradient.Spectrum [^1].RgbColor, scene.Frames [^1].CharacterVisual.ColorCode);
Assert.Contains ("z", scene.Frames [^1].CharacterVisual.Symbol);
}
[Fact]
public void TestAnimationInit ()
{
var animation = character.Animation;
Assert.Equal (character, animation.Character);
Assert.Empty (animation.Scenes);
Assert.Null (animation.ActiveScene);
Assert.False (animation.UseXtermColors);
Assert.False (animation.NoColor);
Assert.Empty (animation.XtermColorMap);
Assert.Equal (0, animation.ActiveSceneCurrentStep);
}
[Fact]
public void TestAnimationNewScene ()
{
var animation = character.Animation;
var scene = animation.NewScene (id:"test_scene", isLooping: true);
Assert.IsType<Scene> (scene);
Assert.Equal ("test_scene", scene.SceneId);
Assert.True (scene.IsLooping);
Assert.True (animation.Scenes.ContainsKey ("test_scene"));
}
[Fact]
public void TestAnimationNewSceneWithoutId ()
{
var animation = character.Animation;
var scene = animation.NewScene ();
Assert.IsType<Scene> (scene);
Assert.Equal ("0", scene.SceneId);
Assert.True (animation.Scenes.ContainsKey ("0"));
}
[Fact]
public void TestAnimationQueryScene ()
{
var animation = character.Animation;
var scene = animation.NewScene (id:"test_scene", isLooping: true);
Assert.Equal (scene, animation.QueryScene ("test_scene"));
}
[Fact]
public void TestAnimationLoopingActiveSceneIsComplete ()
{
var animation = character.Animation;
var scene = animation.NewScene (id: "test_scene", isLooping: true);
scene.AddFrame (symbol: "a", duration: 2);
animation.ActivateScene (scene);
Assert.True (animation.ActiveSceneIsComplete ());
}
[Fact]
public void TestAnimationNonLoopingActiveSceneIsComplete ()
{
var animation = character.Animation;
var scene = animation.NewScene (id: "test_scene");
scene.AddFrame (symbol: "a", duration: 1);
animation.ActivateScene (scene);
Assert.False (animation.ActiveSceneIsComplete ());
animation.StepAnimation ();
Assert.True (animation.ActiveSceneIsComplete ());
}
}

View File

@@ -1,4 +1,6 @@
namespace Terminal.Gui.TextEffects.Tests;
using Terminal.Gui.Drawing;
namespace Terminal.Gui.TextEffects.Tests;
public class GradientFillTests
{
@@ -9,8 +11,8 @@ public class GradientFillTests
// Define the colors of the gradient stops
var stops = new List<Color>
{
Color.FromRgb(255, 0, 0), // Red
Color.FromRgb(0, 0, 255) // Blue
new Color(255, 0, 0), // Red
new Color(0, 0, 255) // Blue
};
// Define the number of steps between each color