Recently I noticed a green-red gradient that I was using wasn’t really what I wanted.
![]()
I wanted it to go through yellow. I have made a Vista sidebar gadget in the past that shows different colours of the horizontal percentage bars for CPU and memory usage which faded from green at 0% to yellow at 50% and then faded from yellow at 50% to red at 100%. I had thought this solved the problem until I tried to use that same formula for a gradient, which turned out to be a triangular gradient.
![]()
The problem here is that there is only yellow at the very peak of the triangle so it looks pinched. From here it is obvious that a curve is needed. I first looked into Bezier curves, as you can join two of them easily by using the same points on both. However, this seemed a bit complicated. I next used a bell curve, which is actually a Gaussian function. This function is e-x2. This worked well and I used it throughout the development of this gradient but after I was finished I realised that a simple Sine wave from 0 to PI would have sufficed (and produces almost exactly the same result as a Gaussian function). A better function would be a Cosine wave from -PI to PI, as this gives a smooth gradient at either end that repeats perfectly. However, this would need to be normalised so that it takes a percent from 0.0 to 1.0 and outputs a value from 0.0 to 1.0 y = (cos((x*2-1)*pi)+1)/2, which is easy to do in a simple Sine 0 to PI because it is done just by multiplying the input by PI.
The key to this is that when it hits the peak, at 50%, it changes from a green-yellow gradient to a red-yellow gradient. The sine function is not used directly to determine the colour but rather to determine where on a simple colour gradient to choose the colour from, which allows it to be used with any combination of colours. The end result is this:
(sine)
(cosine)
C# code listing (for ASPX) is as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | using System; using System.Drawing; using System.Drawing.Imaging; public partial class PercentBar : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { using (Bitmap bmp = new Bitmap(100, 20, PixelFormat.Format24bppRgb)) { double w = (double)bmp.Width; for (int x = 0; x < bmp.Width; x++) { Color c = GetTriColour(x / w, Color.Lime, Color.Yellow, Color.Red); for (int y = 0; y < bmp.Height; y++) bmp.SetPixel(x, y, c); } using (System.IO.MemoryStream ms = new System.IO.MemoryStream()) { bmp.Save(ms, ImageFormat.Png); ms.WriteTo(Response.OutputStream); } Response.ContentType = "image/png"; } } public static Color GetTriColour(double percent, Color left, Color centre, Color right) { if (percent < 0 || percent > 1) throw new Exception("Percent must be between 0 and 1"); //double weight = Math.Sin(percent * Math.PI); double weight = (Math.Cos((percent * 2 - 1) * Math.PI) + 1) / 2; return GetColourFromLinearGradient(weight, percent < 0.5 ? left : right, centre); } public static Color GetColourFromLinearGradient(double percent, Color start, Color end) { double a, r, g, b; if (percent < 0 || percent > 1) throw new Exception("Percent must be between 0 and 1"); double npercent = 1.0 - percent; a = Math.Min(start.A, end.A) + Math.Abs(start.A - end.A) * (start.A > end.A ? npercent : percent); r = Math.Min(start.R, end.R) + Math.Abs(start.R - end.R) * (start.R > end.R ? npercent : percent); g = Math.Min(start.G, end.G) + Math.Abs(start.G - end.G) * (start.G > end.G ? npercent : percent); b = Math.Min(start.B, end.B) + Math.Abs(start.B - end.B) * (start.B > end.B ? npercent : percent); return Color.FromArgb((int)a, (int)r, (int)g, (int)b); } } |