Tuesday, June 7, 2011

Simple Data Visualization using F# & WPF/Silverlight

For several weeks now I've been getting back into Data Visualization
projects (on the weekends). I've also gone back to basics by re-reading
Ben Fry's Visualizing Data while sketching in Processing. Re-gaining the
data visualization perspective has helped me out recently in my WPF
application that I'm working on (day job). The scenario was quite simple
and I could have used any standard WPF control to solve the problem
but I wanted to do something that convey'd the data in a more obvious
way. So I went looking around the web for inspiration and came across
the following flowing data post.


The visualization above is (in my opinion) a great way to compare two
values of information to one another with out using standard controls.
In the above situation the two values are likes vs dislikes. Straight
away we can get a sense (as a end user) of how many people like a
video vs dislike a video based on color. In my WPF application I
need to do something similar. I need to show how many 3D geometric
objects on a particular 3D Model are being used in other parts
of the system vs how many 3D objects are not being used at all in other
parts of the system. So I thought to myself, how might I be able to pull
off the above visualization in WPF? I fired up blend and started sketching.
After about thirty minutes I came up with a similar yet simpler visualization.

You can probably tell from the blend screen shots above
that we can use a simple gradient color to represent the
two values that we need to visualize for the user to
compare. So I tried creating a border with a large corner
radius of about 90 to get a circle that I can apply a border
gradient color to. Unfortunately I wasn't able to get the
effect that I wanted so I tried something simpler below
the border. I created a simple rectangle and applied the
gradient color to the fill of the rectangle. This allowed me
to visually pull off the similar effect in the flowing data post!
Now that I have a visual way to do this I just need to apply
the data so that the rectangle gradient changes dynamically.

#I "path to assemblies below (here)"

#r "PresentationFramework.dll"
#r "WindowsBase.dll"
#r "PresentationCore.dll"

open System
open System.Windows
open System.Windows.Shapes
open System.Windows.Media
open System.Windows.Controls

let r = new Random()
let total_count_of_all_objects = 4556

(* Helper functions - remember the (: byte) & (: float)
   is just annotation for the return type of the function *)
let to_byte (v: int) : byte = Convert.ToByte(v)
let to_float (v: int) : float = Convert.ToDouble(v)

let to_color (r: int) (g: int) (b: int) : Color =
    Color.FromRgb(r |> to_byte, g |> to_byte, b |> to_byte)

(* Tuple to hold total number of objects used in the 3D Model
(random) and total number of objects in the 3D Model *)
let used_total = (r.Next(2000,4556),total_count_of_all_objects)

(* Calculate the number of objects used relative to the total *)
let simulation_result (used_total: int * int): float =
    let used,total = used_total
    used * 100 / total |> to_float

let construct_visualization (used_total: int * int): Grid =
    (* Get simulation result and convert to value between 0 and 1
       for the gradient stops to understand *)
    let v = simulation_result used_total / 100.0
    let actual_color =  to_color 3 129 51
    let total_color =  to_color 255 5 5
    let start_point = new Point(0.0, -0.925) //gradient start point
    let end_point = new Point(1.0, - 0.893) // gradient end point
    let brush = new LinearGradientBrush(actual_color, total_color, start_point, end_point)
    let gradientStopColor1 = new GradientStop(actual_color, v)
    let gradientStopColor2 = new GradientStop(total_color, v)
    brush.GradientStops.Add gradientStopColor1
    brush.GradientStops.Add gradientStopColor2
    let rect = new Rectangle(Fill=brush)
    let grd = new Grid(Width=260.0, Height=30.0,

    grd.Children.Add rect |> ignore
let shell = new Window(Width=300.0,Height=300.0)
let viz = construct_visualization used_total
shell.Content <- viz

[] ignore <| (new Application()).Run shell

It's worth noting that even though this was shown as a WPF sample it
could easily be applied to Silverlight, which is why I included it in the title
of the post. =)

F# is great for prototyping and executing ideas! 
Just seeing something on the web and hopping right
into Blend/VS2010 to execute an idea in about an 
hour is why I love this language on top of this platform.

Until next time...
-Develop with passion

No comments:

Post a Comment