Blog

Seasons Greetings...and Charts!

It's that time of year again, the first snow has fallen (at least in these parts of the world) and the shop windows are packed with Christmas decorations. It's still business as usual at Visiblox HQ...well for the most part. Some of the elves took a little time out to provide our blog with a bit of Christmas spirit.

Playtime!

What does Christmas have to do with Visiblox Charts, you ask? Check it out!

Instructions for use:

  1. Decorate your Christmas tree with candles and decorations
  2. If you don't like it or want to try again, select the Santa hand, shake the tree and start again! (Note, all decorations are made of a durable titanium alloy and so will not break or dent on impact - just pick them back up and place them on the tree if desired...yes, even the candles...)

Falling Snowflakes

All of the above example was done using standard features of Visiblox Charts 2.1. If you're curious about how, I'll take you through the implementation now.

First of all we set about putting falling snowflakes in the background of the chart. Note that it's not just an animated gif or something, it's actual silverlight controls falling. I found some excellent code which was used as a basis for this functionality here. In order to use that I created a SnowflakePanel class and retemplated the chart to put a snowflake panel in the background.

We won't reproduce the whole chart template here (the code is available for download in full here- note you'll need the premium version of Visiblox Charts) but the relevant piece is this:

    <primitives:LegendLayoutContainer.ChartContent>
        <Grid Name="PlotArea" Background="Transparent" primitives:Clip.ToBounds="True" Style="{TemplateBinding PlotAreaStyle}">
              <local:SnowfallPanel Background="{StaticResource NightSky}" />
              <Border Name="PlotAreaBorder" Style="{TemplateBinding PlotAreaBorderStyle}" Canvas.ZIndex="120" />
              <Grid Name="SeriesContainer" />
              <Grid Name="GridlinesContainer" Canvas.ZIndex="-100" />
              <Canvas Name="BehaviourContainer" />
              <Grid Name="AnnotationsContainer" />
         </Grid>
     </primitives:LegendLayoutContainer.ChartContent>

At this point, the chart will render with a background of falling snowflakes.

The Tree

The tree is made up of 2 standard Visiblox Charts LineSeries with ShowArea set to true. We set the area fill and line colours to green and brown respectively for the tree and trunk. After that it's just a matter of shaping the tree with appropriate points using standard DataSeries. In order to keep things simple we define axes with explicit ranges from 0 to 10 for both X and Y.

We define the data points in the MainPage constructor in code behind:

            //these are the coordinates of the tree
            _tree.Add(2, 1);
            _tree.Add(4, 2.5);
            _tree.Add(2.5, 2.5);
            _tree.Add(4.3, 4);
            _tree.Add(3, 4);
            _tree.Add(5, 5.5);
            _tree.Add(7, 4);
            _tree.Add(5.7, 4);
            _tree.Add(7.5, 2.5);
            _tree.Add(6, 2.5);
            _tree.Add(8, 1);
            _tree.Add(2, 1);

            //these are the coordinates of the trunk
            _trunk.Add(4.5, 0);
            _trunk.Add(4.5, 1);
            _trunk.Add(5.5, 1);
            _trunk.Add(5.5, 0);
            _trunk.Add(4.5, 0);

            //create a gradient brush to fill in the tree
            LinearGradientBrush treeBrush = new LinearGradientBrush();
            treeBrush.StartPoint = new Point(0.5,0);
            treeBrush.EndPoint = new Point(0.5,1);
            GradientStop lightGreen = new GradientStop();
            lightGreen.Color = Color.FromArgb(255,0,255,0);
            lightGreen.Offset = 0;
            GradientStop darkGreen = new GradientStop();
            darkGreen.Color = Color.FromArgb(255,0,100,0);
            darkGreen.Offset = 1;
            treeBrush.GradientStops.Add(lightGreen);
            treeBrush.GradientStops.Add(darkGreen);

            //create a series for the tree and trunk

            LineSeries xmasTree = new LineSeries() { 
                DataSeries = _tree, 
                LineStrokeThickness = 3, 
                LineStroke = new SolidColorBrush(Colors.Green), 
                AreaFill = treeBrush,
                ShowArea = true
            };

            LineSeries trunk = new LineSeries()
            {
                DataSeries = _trunk,
                LineStrokeThickness = 3,
                LineStroke = new SolidColorBrush(Color.FromArgb(255,122,52,5)),
                AreaFill = new SolidColorBrush(Color.FromArgb(255, 122, 52, 5)),
                ShowArea = true
            };

            XmasChart.Series.Add(xmasTree);
            XmasChart.Series.Add(trunk);

At this point we have a Christmas tree, in a night sky with gentle falling snowflakes. Lets get to the decorating!

The Decorations

The decorations are implemented using a special ImageAnnotation class, which we borrowed from our previous post about extending annotations. All we do is grab a few clip art images and stick them in a list box with a data template by binding to a collection of Decoration objects. On selection change we change which decoration is attached to the AnnotationBehaviour, which we attached to the chart in XAML. We also define a custom AnnotationFactory so that the AnnotationBehaviour knows which annotation type to create.

That works fine, but the annotations hang in mid-air if you don't put them on the tree. That won't do!

Gravity Abides

To avoid the "hanging annotation" problem, we implement a simulation of gravity. This is fairly complicated in code behind, but mostly because Silverlight doesn't seem to offer much in the way of geometry support. So there is a fair chunk of code in the example to find the intersection between two lines and to determine whether the annotation was placed inside the tree in the first place. Again, we won't replicate all that code here, feel free to download the source and have a look though. Once we detect that it is in mid-air, and have determined how far to let it fall, we then make it happen.

To make it fall we use a DoubleAnimation to modify the Y value of the data point to which the annotation is attached. However, there is a snag. The default implementation of DataPoint is not a DependencyObject which is required by the Silverlight animation framework to work. To get around that we borrow the notion of a DataPointWrapper from our blog post on animating Visiblox Charts.

We can then simply use that to animate the data point, which in turn causes the annotation to update its position.

private static void AnimateAnnotation(DataPoint<IComparable, IComparable> dp, double toValue)
        {
            //calculate the time it takes for the decoration to reach its new height
            double distance = (double)dp.Y - toValue;
            long interval = 300;
            //should always be true but just in case
            //acceleration
            double g = 0.00002;
            if (distance >0) {
                interval = (long)Math.Sqrt(distance/g);
            }
            
            Storyboard sb = new Storyboard();
            DoubleAnimation anim = new DoubleAnimation() { To = toValue, EasingFunction = new QuadraticEase() { EasingMode = EasingMode.EaseIn }, Duration = TimeSpan.FromMilliseconds(interval) };
            DataPointWrapper dpw = new DataPointWrapper(dp);
            Storyboard.SetTarget(anim, dpw);
            Storyboard.SetTargetProperty(anim, new PropertyPath("YValue"));
            sb.Children.Add(anim);
            sb.Completed += Animation_Completed;
            //add a reference to the datapoint wrapper to prevent garbage collection
            animations[sb] = dpw;
            sb.Begin();
        }

The Hand of Santa

So that just leaves us with clearing the tree. To do that we add a PanBehaviour to the chart with only X-axis pan enabled. When the hand toggle button is selected, the PanBehaviour is enabled and the AnnotationBehaviour is disabled. We then register an event listener for X-axis range events. Whenever the X-axis Effective range changes (which describes the visible portion of the chart) we determine whether the pan is going in the same direction as last time we saw it. If not, we count that as a change of direction. If we see 3 or more changes of direction, we need to clear the tree.

To do that we just iterate over the Chart.Annotations collection and animate all of their positions to 0, thus making them fall to the ground.

Summary

In this post we've created a Christmas tree on a chart. Clearly, it's not a realistic charting requirement, but it's seasonal, it was fun to do and it demonstrates just how flexible Visiblox Charts really is! As always you can download the source for this example here. Note that you will need to add a reference to the premium edition of Visiblox Charts to use this.

In the meantime, from everyone at Visiblox: Have a very Merry Christmas and a Happy New Year!!!

Comments

Cute, but when's the next release coming out? :-)

Would be great if it supported a BandSeries with a Staircase Style (plotting two Staircase series over each other with a fill for the area in between absolutely kills performance when running under Citrix).

 
Posted by James

Hi Jim,

Thanks, it was fun :) Regarding the next release, it's currently looking like mid-Q1 2012 - we'll have a more accurate release date in the new year.

As for the BandSeries/Staircase combo I'll make sure to log that as a feature request for you, thanks for the suggestion.

Jesse

 
Posted by Jesse Beaumont

This was a fun post, thanks for sharing!

 
Posted by Adam

Post a comment