I recently faced a visualisation task which required me to set Colors on shapes according to the strength of a value, this kind of thing is not uncommon in dashboard making.
I’ve changed the colors here but the essence of the problem was that I had a scale which looked something like this (actual colors changed):
In other words a gradient brush scale, such as:
<LinearGradientBrush x:Key="MyTestBrush" StartPoint="0.05,0" EndPoint="0.95,0">
<GradientStop Offset="0" Color="Red"/>
<GradientStop Offset="0.5" Color="Green"/>
<GradientStop Offset="1" Color="Blue"/>
</LinearGradientBrush>
In my case the brush had far more colors than this in its scale and the particular offsetting of the start and the end of the gradient was important.
I’d previously stumbled on this post about making custom interpolators for the Silverlight Data Visualization Toolkit.
So I set about the task of making a LinearGradientBrushInterpolator trying to re-use as much as possible from the toolkit itself.
First off I looked at the way the SolidColorBrushInterpolator worked, it already did a big part of what I needed to do by interpolating a range between 2 different colors.
[sourcecode language='csharp' padlinenumbers='true' light='false' autolink='false' gutter='false']
namespace ColorPicker
{
public class LinearGradientBrushInterpolator : Interpolator
{
System.Collections.Generic.List interpolators = new System.Collections.Generic.List();
LinearGradientBrush templateBrush;
public LinearGradientBrush TemplateBrush
{
get
{
return templateBrush;
}
set
{
if (value != templateBrush)
{
templateBrush = value;
interpolators.Clear();
}
}
}
}
}
[/sourcecode]
In other words I decided to use an array of SolidBrushInterpolators to do this. To set these up I wrote this:
[sourcecode language='csharp' htmlscript='false' padlinenumbers='true']
private void InitializeBrush(LinearGradientBrush br)
{
if (br == null)
throw new ArgumentNullException("br");
//set start and end interpolators
double start = br.StartPoint.X;
double end = br.EndPoint.X;
if (br.GradientStops.Count < 2)
{
throw new Exception("2 Gradiant Stops must exist");
}
SolidColorBrushInterpolator startInterpolator = new SolidColorBrushInterpolator();
startInterpolator.DataMinimum = start;
startInterpolator.DataMaximum = br.GradientStops[1].Offset;
startInterpolator.From = br.GradientStops[0].Color;
startInterpolator.To = br.GradientStops[1].Color;
interpolators.Add(startInterpolator);
for (int i = 1; i < br.GradientStops.Count - 1; i++)
{
SolidColorBrushInterpolator inter = new SolidColorBrushInterpolator();
inter.DataMinimum = br.GradientStops[i].Offset;
inter.DataMaximum = br.GradientStops[i + 1].Offset;
inter.From = br.GradientStops[i].Color;
inter.To = br.GradientStops[i + 1].Color;
interpolators.Add(inter);
}
}
[/sourcecode]
Because of my Gradients not starting or ending on 0 or 1 I had to add special handling of these beginning and end cases.
Finally I had to implement the Interpolate method itself:
[sourcecode language='csharp' ]
public override object Interpolate(double value)
{
if (interpolators.Count == 0)
{
InitializeBrush(templateBrush);
if (interpolators.Count == 0)
{
throw new InvalidOperationException("TemplateBrush is not optional");
}
}
double min = 0;
double max = 0;
if (double.IsNaN(DataMinimum))
{
min = ActualDataMinimum;
}
if (double.IsNaN(DataMinimum))
{
max = ActualDataMaximum;
}
if (value < min)
{
value = min;
}
if (value > max)
{
value = max;
}
//calculate relative interpolation
double offset = (value - min) / (max - min);
foreach (SolidColorBrushInterpolator inter in interpolators)
{
if (offset < inter.DataMaximum)
{
if (offset < inter.DataMinimum)
offset = inter.DataMinimum;
return inter.Interpolate(offset);
}
}
return interpolators[interpolators.Count - 1].Interpolate(interpolators[interpolators.Count - 1].DataMaximum);
}
[/sourcecode]
The end result worked nicely and I put together this TreeMap to prove it to myself:
The live version of this sample (which randomizes each time you refresh is here).
Full Source code including the sample application is here.
2 responses so far ↓
1 http://tinyurl.com/lmddfagan35453 // Jan 27, 2013 at 1:20 pm
“Silverlight Color Interpolation | Sam Winstanley’s Blog” was certainly pleasurable and informative! In modern society honestly, that is really hard to do. With thanks, Tabitha
2 Personal Site // Apr 28, 2013 at 10:32 pm
I am not sure where you’re getting your info, but good topic. I needs to spend some time learning much more or understanding more. Thanks for fantastic info I was looking for this info for my mission.
Here is my blog Personal Site
Leave a Comment