I use the following c# class to scale between 3 colours (typically red for negative, yellow for the mid point and green for positive numbers:
using System.Drawing;
namespace Utils.Maths;
public class ColourScaler
{
public static (int r, int g, int b) HighestColor = (99, 190, 123);
public static (int r, int g, int b) MidColor = (255, 235, 132);
public static (int r, int g, int b) LowestColor = (248, 105, 107);
public double MidValue { get; set; } = 0;
public double HighestValue { get; set; } = 0;
public double LowestValue { get; set; } = 0;
public IEnumerable<double> Values
{
set
{
var items = value.OrderBy(o => o).Distinct().ToArray();
if (items.Length > 0)
{
LowestValue= items[0];
HighestValue = items[items.Length - 1];
}
}
}
public ColourScaler(IEnumerable<double>? values = null, double midValue = 0)
{
Values = values ?? Enumerable.Empty<double>();
MidValue = midValue;
}
public (int r, int g, int b) GetRgb(double value)
{
if (value < LowestValue || value > HighestValue)
throw new ArgumentOutOfRangeException().AddData("value", value);
if (value >= 0)
{
if (HighestValue - MidValue == 0)
return HighestColor;
var ratio = (value - MidValue) / (HighestValue - MidValue);
var r = (int)(MidColor.r - (MidColor.r - HighestColor.r) * ratio);
var g = (int)(MidColor.g - (MidColor.g - HighestColor.g) * ratio);
var b = (int)(MidColor.b - (MidColor.b - HighestColor.b) * ratio);
return (r, g, b);
}
else
{
if (MidValue - LowestValue == 0)
return LowestColor;
var ratio = (MidValue - value) / (MidValue - LowestValue);
var r = (int)(MidColor.r - (MidColor.r - LowestColor.r) * ratio);
var g = (int)(MidColor.g - (MidColor.g - LowestColor.g) * ratio);
var b = (int)(MidColor.b - (MidColor.b - LowestColor.b) * ratio);
return (r, g, b);
}
}
public Color GetColour(double value)
{
var rgb = GetRgb(value);
return Color.FromArgb(rgb.r, rgb.g, rgb.b);
}
}
And here are some tests:
namespace Tests.ColourScaler;
[TestClass]
public class ColourScalerTests
{
[TestMethod]
public void ColourScaler_Values()
{
var item = new ColourScaler { Values = new double[] { 13, -2, -3, 4, 3, -3, 4, 1, 0 } };
Assert.AreEqual(-3, item.LowestValue);
Assert.AreEqual(0, item.MidValue);
Assert.AreEqual(13, item.HighestValue);
item = new ColourScaler { Values = new double[] { } };
Assert.AreEqual(0, item.LowestValue);
Assert.AreEqual(0, item.MidValue);
Assert.AreEqual(0, item.HighestValue);
}
[TestMethod]
public void ColourScaler_New()
{
var item = new ColourScaler(new double[] { 13, -2, -3, 4, 3, -3, 4, 1, 0 }, 1);
Assert.AreEqual(-3, item.LowestValue);
Assert.AreEqual(1, item.MidValue);
Assert.AreEqual(13, item.HighestValue);
item = new ColourScaler(new double[] { 13, -2, -3, 4, 3, -3, 4, 1, 0 });
Assert.AreEqual(-3, item.LowestValue);
Assert.AreEqual(0, item.MidValue);
Assert.AreEqual(13, item.HighestValue);
item = new ColourScaler(new double[] { });
Assert.AreEqual(0, item.LowestValue);
Assert.AreEqual(0, item.MidValue);
Assert.AreEqual(0, item.HighestValue);
}
[TestMethod]
public void ColourScaler_GetRgb()
{
var item = new ColourScaler { Values = new double[] { -7, -5, -3, -1, 0, 2, 4, 6 } };
var rgb = item.GetRgb(-7);
Assert.AreEqual(248, rgb.r);
Assert.AreEqual(105, rgb.g);
Assert.AreEqual(107, rgb.b);
rgb = item.GetRgb(-6);
Assert.AreEqual(249, rgb.r);
Assert.AreEqual(123, rgb.g);
Assert.AreEqual(110, rgb.b);
rgb = item.GetRgb(-5);
Assert.AreEqual(250, rgb.r);
Assert.AreEqual(142, rgb.g);
Assert.AreEqual(114, rgb.b);
rgb = item.GetRgb(-2);
Assert.AreEqual(253, rgb.r);
Assert.AreEqual(197, rgb.g);
Assert.AreEqual(124, rgb.b);
rgb = item.GetRgb(-1);
Assert.AreEqual(254, rgb.r);
Assert.AreEqual(216, rgb.g);
Assert.AreEqual(128, rgb.b);
rgb = item.GetRgb(0);
Assert.AreEqual(255, rgb.r);
Assert.AreEqual(235, rgb.g);
Assert.AreEqual(132, rgb.b);
rgb = item.GetRgb(1);
Assert.AreEqual(229, rgb.r);
Assert.AreEqual(227, rgb.g);
Assert.AreEqual(130, rgb.b);
rgb = item.GetRgb(2);
Assert.AreEqual(203, rgb.r);
Assert.AreEqual(220, rgb.g);
Assert.AreEqual(129, rgb.b);
rgb = item.GetRgb(4);
Assert.AreEqual(151, rgb.r);
Assert.AreEqual(205, rgb.g);
Assert.AreEqual(126, rgb.b);
rgb = item.GetRgb(5);
Assert.AreEqual(125, rgb.r);
Assert.AreEqual(197, rgb.g);
Assert.AreEqual(124, rgb.b);
rgb = item.GetRgb(6);
Assert.AreEqual(99, rgb.r);
Assert.AreEqual(190, rgb.g);
Assert.AreEqual(123, rgb.b);
}
}
255 0 0
.... 50% is0 255 0
and 100% is0 0 255
right? therefore 15% would fall between255 0 0
and0 255 0
so in my mind thats(255 * 0.15) (255 * 0.15) 0
.... sorry if ive completely misunderstood (also the 0.15's seem slightly off to me in my own logic) – Babby