Gradient tests

This commit is contained in:
tznind
2024-07-07 21:06:21 +01:00
parent a167366b14
commit 116cba8c8c
3 changed files with 189 additions and 19 deletions

View File

@@ -44,15 +44,42 @@ public class Gradient
private readonly List<int> _steps;
/// <summary>
/// Creates a new instance of the <see cref="Gradient"/> class which hosts a <see cref="Spectrum"/>
/// of colors including all <paramref name="stops"/> and <paramref name="steps"/> interpolated colors
/// between each corresponding pair.
/// </summary>
/// <param name="stops">The colors to use in the spectrum (N)</param>
/// <param name="steps">The number of colors to generate between each pair (must be N-1 numbers).
/// If only one step is passed then it is assumed to be the same distance for all pairs.</param>
/// <param name="loop">True to duplicate the first stop and step so that the gradient repeats itself</param>
/// <exception cref="ArgumentException"></exception>
public Gradient (IEnumerable<Color> stops, IEnumerable<int> steps, bool loop = false)
{
_stops = stops.ToList ();
if (_stops.Count < 1)
{
throw new ArgumentException ("At least one color stop must be provided.");
}
_steps = steps.ToList ();
// If multiple colors and only 1 step assume same distance applies to all steps
if (_stops.Count > 2 && _steps.Count == 1)
{
_steps = Enumerable.Repeat (_steps.Single (),_stops.Count() - 1).ToList();
}
if (_steps.Any (step => step < 1))
{
throw new ArgumentException ("Steps must be greater than 0.");
}
if (_steps.Count != _stops.Count - 1)
{
throw new ArgumentException ("Number of steps must be N-1");
}
_loop = loop;
Spectrum = GenerateGradient (_steps);
@@ -85,6 +112,7 @@ public class Gradient
private List<Color> GenerateGradient (IEnumerable<int> steps)
{
List<Color> gradient = new List<Color> ();
if (_stops.Count == 1)
{
for (int i = 0; i < steps.Sum (); i++)
@@ -94,13 +122,17 @@ public class Gradient
return gradient;
}
var stopsToUse = _stops.ToList ();
var stepsToUse = _steps.ToList ();
if (_loop)
{
_stops.Add (_stops [0]);
stopsToUse.Add (_stops [0]);
stepsToUse.Add (_steps.First ());
}
var colorPairs = _stops.Zip (_stops.Skip (1), (start, end) => new { start, end });
var stepsList = _steps.ToList ();
var colorPairs = stopsToUse.Zip (stopsToUse.Skip (1), (start, end) => new { start, end });
var stepsList = stepsToUse;
foreach (var (colorPair, thesteps) in colorPairs.Zip (stepsList, (pair, step) => (pair, step)))
{
@@ -112,7 +144,7 @@ public class Gradient
private IEnumerable<Color> InterpolateColors (Color start, Color end, int steps)
{
for (int step = 0; step <= steps; step++)
for (int step = 0; step < steps; step++)
{
double fraction = (double)step / steps;
int r = (int)(start.R + fraction * (end.R - start.R));
@@ -120,8 +152,10 @@ public class Gradient
int b = (int)(start.B + fraction * (end.B - start.B));
yield return new Color (r, g, b);
}
yield return end; // Ensure the last color is included
}
/// <summary>
/// <para>
/// Creates a mapping starting at 0,0 and going to <paramref name="maxRow"/> and <paramref name="maxColumn"/>

View File

@@ -14,6 +14,8 @@ public class TextEffectsScenario : Scenario
{
private TabView tabView;
public static bool LoopingGradient = false;
public override void Main ()
{
Application.Init ();
@@ -27,8 +29,6 @@ public class TextEffectsScenario : Scenario
w.Loaded += (s, e) =>
{
SetupGradientLineCanvas (w, w.Frame.Size);
// TODO: Does not work
// SetupGradientLineCanvas (tabView, tabView.Frame.Size);
};
w.SizeChanging += (s, e) =>
{
@@ -36,9 +36,6 @@ public class TextEffectsScenario : Scenario
{
SetupGradientLineCanvas (w, e.Size.Value);
}
// TODO: Does not work
//SetupGradientLineCanvas (tabView, tabView.Frame.Size);
};
w.ColorScheme = new ColorScheme
@@ -57,16 +54,32 @@ public class TextEffectsScenario : Scenario
Height = Dim.Fill (),
};
var gradientsView = new GradientsView ()
{
Width = Dim.Fill (),
Height = Dim.Fill (),
};
var t1 = new Tab ()
{
View = new GradientsView ()
{
Width = Dim.Fill (),
Height = Dim.Fill (),
},
View = gradientsView,
DisplayText = "Gradients"
};
var cbLooping = new CheckBox ()
{
Text = "Looping",
Y = Pos.AnchorEnd (1)
};
cbLooping.Toggle += (s, e) =>
{
LoopingGradient = e.NewValue == CheckState.Checked;
SetupGradientLineCanvas (w, w.Frame.Size);
tabView.SetNeedsDisplay ();
};
gradientsView.Add (cbLooping);
tabView.AddTab (t1, false);
w.Add (tabView);
@@ -83,7 +96,7 @@ public class TextEffectsScenario : Scenario
{
GetAppealingGradientColors (out var stops, out var steps);
var g = new Gradient (stops, steps);
var g = new Gradient (stops, steps, LoopingGradient);
var fore = new GradientFill (
new Rectangle (0, 0, size.Width, size.Height), g, GradientDirection.Diagonal);
@@ -107,7 +120,8 @@ public class TextEffectsScenario : Scenario
};
// Define the number of steps between each color for smoother transitions
steps = new List<int> { 15, 15, 15, 15 };
// If we pass only a single value then it will assume equal steps between all pairs
steps = new List<int> { 15 };
}
}
@@ -157,7 +171,7 @@ internal class GradientsView : View
var steps = new List<int> { 10, 10 }; // 10 steps between Red -> Green, and Green -> Blue
// Create the gradient
var radialGradient = new Gradient (stops, steps, loop: false);
var radialGradient = new Gradient (stops, steps, loop: TextEffectsScenario.LoopingGradient);
// Define the size of the rectangle
int maxRow = 15; // Adjusted to keep aspect ratio
@@ -207,7 +221,7 @@ internal class GradientsView : View
};
// Create the gradient
var rainbowGradient = new Gradient (stops, steps, loop: true);
var rainbowGradient = new Gradient (stops, steps, TextEffectsScenario.LoopingGradient);
for (int x = 0; x < viewport.Width; x++)
{

View File

@@ -50,4 +50,126 @@ public class GradientTests
Assert.Equal (c.Key, new Point(0,0));
Assert.Equal (c.Value, new Color (0, 0, 255));
}
}
[Fact]
public void SingleColorStop ()
{
var stops = new List<Color> { new Color (255, 0, 0) }; // Red
var steps = new List<int> { };
var g = new Gradient (stops, steps, loop: false);
Assert.All (g.Spectrum, color => Assert.Equal (new Color (255, 0, 0), color));
}
[Fact]
public void LoopingGradient_CorrectColors ()
{
var stops = new List<Color>
{
new Color(255, 0, 0), // Red
new Color(0, 0, 255) // Blue
};
var steps = new List<int> { 10 };
var g = new Gradient (stops, steps, loop: true);
Assert.Equal (new Color (255, 0, 0), g.Spectrum.First ());
Assert.Equal (new Color (255, 0, 0), g.Spectrum.Last ());
}
[Fact]
public void DifferentStepSizes ()
{
var stops = new List<Color>
{
new Color(255, 0, 0), // Red
new Color(0, 255, 0), // Green
new Color(0, 0, 255) // Blue
};
var steps = new List<int> { 5, 15 }; // Different steps
var g = new Gradient (stops, steps, loop: false);
Assert.Equal (22, g.Spectrum.Count);
}
[Fact]
public void FractionOutOfRange_ThrowsException ()
{
var stops = new List<Color>
{
new Color(255, 0, 0), // Red
new Color(0, 0, 255) // Blue
};
var steps = new List<int> { 10 };
var g = new Gradient (stops, steps, loop: false);
Assert.Throws<ArgumentOutOfRangeException> (() => g.GetColorAtFraction (-0.1));
Assert.Throws<ArgumentOutOfRangeException> (() => g.GetColorAtFraction (1.1));
}
[Fact]
public void NaNFraction_ReturnsLastColor ()
{
var stops = new List<Color>
{
new Color(255, 0, 0), // Red
new Color(0, 0, 255) // Blue
};
var steps = new List<int> { 10 };
var g = new Gradient (stops, steps, loop: false);
Assert.Equal (new Color (0, 0, 255), g.GetColorAtFraction (double.NaN));
}
[Fact]
public void Constructor_SingleStepProvided_ReplicatesForAllPairs ()
{
var stops = new List<Color>
{
new Color(255, 0, 0), // Red
new Color(0, 255, 0), // Green
new Color(0, 0, 255) // Blue
};
var singleStep = new List<int> { 5 }; // Single step provided
var gradient = new Gradient (stops, singleStep, loop: false);
Assert.NotNull (gradient.Spectrum);
Assert.Equal (12, gradient.Spectrum.Count); // 5 steps Red -> Green + 5 steps Green -> Blue + 2 end colors
}
[Fact]
public void Constructor_InvalidStepsLength_ThrowsArgumentException ()
{
var stops = new List<Color>
{
new Color(255, 0, 0), // Red
new Color(0, 0, 255) // Blue
};
var invalidSteps = new List<int> { 5, 5 }; // Invalid length (N-1 expected)
Assert.Throws<ArgumentException> (() => new Gradient (stops, invalidSteps, loop: false));
}
[Fact]
public void Constructor_ValidStepsLength_DoesNotThrow ()
{
var stops = new List<Color>
{
new Color(255, 0, 0), // Red
new Color(0, 255, 0), // Green
new Color(0, 0, 255) // Blue
};
var validSteps = new List<int> { 5, 5 }; // Valid length (N-1)
var gradient = new Gradient (stops, validSteps, loop: false);
Assert.NotNull (gradient.Spectrum);
Assert.Equal (12, gradient.Spectrum.Count); // 5 steps Red -> Green + 5 steps Green -> Blue + 2 end colors
}
}