Sam Winstanley’s Blog

Market Research Technology News and Views

Sam Winstanley’s Blog header image 2

Silverlight Color Interpolation

May 28th, 2010 · 3 Comments

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):

image

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:

image

The live version of this sample (which randomizes each time you refresh is here).

Full Source code including the sample application is here.

Tags: Uncategorized

3 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 praveen // Mar 26, 2014 at 5:28 pm

    Hi,

    Thanks you so much for this source Code. However, Interpolation is not working for me when I set the DataContext outside the MainPage(). I have to get the data from DB and bind in the DB.

    The values are bounded well, but colors are not set. Can you please help me on this issue?

  • 3 christian louboutin clearance sale // Apr 21, 2014 at 9:09 am

    Vibram Five fingers Womens Sprint (Pink)2013

Leave a Comment