Graphics.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. namespace Terminal.Gui.TextEffects;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. public class Color
  6. {
  7. public string RgbColor { get; private set; }
  8. public int? XtermColor { get; private set; }
  9. public Color (string rgbColor)
  10. {
  11. if (!ColorUtils.IsValidHexColor (rgbColor))
  12. throw new ArgumentException ("Invalid RGB hex color format.");
  13. RgbColor = rgbColor.StartsWith ("#") ? rgbColor.Substring (1).ToUpper () : rgbColor.ToUpper ();
  14. XtermColor = ColorUtils.HexToXterm (RgbColor); // Convert RGB to XTerm-256
  15. }
  16. public Color (int xtermColor)
  17. {
  18. if (!ColorUtils.IsValidXtermColor (xtermColor))
  19. throw new ArgumentException ("Invalid XTerm-256 color code.");
  20. XtermColor = xtermColor;
  21. RgbColor = ColorUtils.XtermToHex (xtermColor); // Perform the actual conversion
  22. }
  23. public int R => Convert.ToInt32 (RgbColor.Substring (0, 2), 16);
  24. public int G => Convert.ToInt32 (RgbColor.Substring (2, 2), 16);
  25. public int B => Convert.ToInt32 (RgbColor.Substring (4, 2), 16);
  26. public (int R, int G, int B) GetRgbInts ()
  27. {
  28. return (
  29. Convert.ToInt32 (RgbColor.Substring (0, 2), 16),
  30. Convert.ToInt32 (RgbColor.Substring (2, 2), 16),
  31. Convert.ToInt32 (RgbColor.Substring (4, 2), 16)
  32. );
  33. }
  34. public override string ToString () => $"#{RgbColor}";
  35. public static Color FromRgb (int r, int g, int b)
  36. {
  37. // Validate the RGB values to ensure they are within the 0-255 range
  38. if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255)
  39. throw new ArgumentOutOfRangeException ("RGB values must be between 0 and 255.");
  40. // Convert RGB values to a hexadecimal string
  41. string rgbColor = $"#{r:X2}{g:X2}{b:X2}";
  42. // Create and return a new Color instance using the hexadecimal string
  43. return new Color (rgbColor);
  44. }
  45. }
  46. public class Gradient
  47. {
  48. public List<Color> Spectrum { get; private set; }
  49. private readonly bool _loop;
  50. private readonly List<Color> _stops;
  51. private readonly List<int> _steps;
  52. public enum Direction
  53. {
  54. Vertical,
  55. Horizontal,
  56. Radial,
  57. Diagonal
  58. }
  59. public Gradient (IEnumerable<Color> stops, IEnumerable<int> steps, bool loop = false)
  60. {
  61. _stops = stops.ToList ();
  62. if (_stops.Count < 1)
  63. throw new ArgumentException ("At least one color stop must be provided.");
  64. _steps = steps.ToList ();
  65. if (_steps.Any (step => step < 1))
  66. throw new ArgumentException ("Steps must be greater than 0.");
  67. _loop = loop;
  68. Spectrum = GenerateGradient (_steps);
  69. }
  70. public Color GetColorAtFraction (double fraction)
  71. {
  72. if (fraction < 0 || fraction > 1)
  73. throw new ArgumentOutOfRangeException (nameof (fraction), "Fraction must be between 0 and 1.");
  74. int index = (int)(fraction * (Spectrum.Count - 1));
  75. return Spectrum [index];
  76. }
  77. private List<Color> GenerateGradient (IEnumerable<int> steps)
  78. {
  79. List<Color> gradient = new List<Color> ();
  80. if (_stops.Count == 1)
  81. {
  82. for (int i = 0; i < steps.Sum (); i++)
  83. {
  84. gradient.Add (_stops [0]);
  85. }
  86. return gradient;
  87. }
  88. if (_loop)
  89. {
  90. _stops.Add (_stops [0]);
  91. }
  92. var colorPairs = _stops.Zip (_stops.Skip (1), (start, end) => new { start, end });
  93. var stepsList = _steps.ToList ();
  94. foreach (var (colorPair, thesteps) in colorPairs.Zip (stepsList, (pair, step) => (pair, step)))
  95. {
  96. gradient.AddRange (InterpolateColors (colorPair.start, colorPair.end, thesteps));
  97. }
  98. return gradient;
  99. }
  100. private IEnumerable<Color> InterpolateColors (Color start, Color end, int steps)
  101. {
  102. for (int step = 0; step <= steps; step++)
  103. {
  104. double fraction = (double)step / steps;
  105. int r = (int)(start.R + fraction * (end.R - start.R));
  106. int g = (int)(start.G + fraction * (end.G - start.G));
  107. int b = (int)(start.B + fraction * (end.B - start.B));
  108. yield return Color.FromRgb (r, g, b);
  109. }
  110. }
  111. public Dictionary<Coord, Color> BuildCoordinateColorMapping (int maxRow, int maxColumn, Direction direction)
  112. {
  113. var gradientMapping = new Dictionary<Coord, Color> ();
  114. switch (direction)
  115. {
  116. case Direction.Vertical:
  117. for (int row = 0; row <= maxRow; row++)
  118. {
  119. double fraction = maxRow == 0 ? 1.0 : (double)row / maxRow;
  120. Color color = GetColorAtFraction (fraction);
  121. for (int col = 0; col <= maxColumn; col++)
  122. {
  123. gradientMapping [new Coord (col, row)] = color;
  124. }
  125. }
  126. break;
  127. case Direction.Horizontal:
  128. for (int col = 0; col <= maxColumn; col++)
  129. {
  130. double fraction = maxColumn == 0 ? 1.0 : (double)col / maxColumn;
  131. Color color = GetColorAtFraction (fraction);
  132. for (int row = 0; row <= maxRow; row++)
  133. {
  134. gradientMapping [new Coord (col, row)] = color;
  135. }
  136. }
  137. break;
  138. case Direction.Radial:
  139. for (int row = 0; row <= maxRow; row++)
  140. {
  141. for (int col = 0; col <= maxColumn; col++)
  142. {
  143. double distanceFromCenter = FindNormalizedDistanceFromCenter (maxRow, maxColumn, new Coord (col, row));
  144. Color color = GetColorAtFraction (distanceFromCenter);
  145. gradientMapping [new Coord (col, row)] = color;
  146. }
  147. }
  148. break;
  149. case Direction.Diagonal:
  150. for (int row = 0; row <= maxRow; row++)
  151. {
  152. for (int col = 0; col <= maxColumn; col++)
  153. {
  154. double fraction = ((double)row * 2 + col) / ((maxRow * 2) + maxColumn);
  155. Color color = GetColorAtFraction (fraction);
  156. gradientMapping [new Coord (col, row)] = color;
  157. }
  158. }
  159. break;
  160. }
  161. return gradientMapping;
  162. }
  163. private double FindNormalizedDistanceFromCenter (int maxRow, int maxColumn, Coord coord)
  164. {
  165. double centerX = maxColumn / 2.0;
  166. double centerY = maxRow / 2.0;
  167. double dx = coord.Column - centerX;
  168. double dy = coord.Row - centerY;
  169. double distance = Math.Sqrt (dx * dx + dy * dy);
  170. double maxDistance = Math.Sqrt (centerX * centerX + centerY * centerY);
  171. return distance / maxDistance;
  172. }
  173. }