It might not sound like the most festive of features, but the recent 3.8 release of KeyLines featured a brand new Sequential layout – designed for handling tiered data with reduced edge crossings.
When tasked with building a demo to showcase its strengths, I noticed that a few of the layouts looked a little bit like Christmas trees. Always keen to take great technology and turn it to something absolutely trivial, I decided to develop this idea into a KeyLines Christmas card.
This whole animation was drawn with a KeyLines chart, a sequential layout, and a whole lot of animated nodes.
We take on a lot of very serious work here at Cambridge Intelligence: working with partners throughout the world in the fields of cyber security, counter-terrorism and fraud detection. As a festive counterpoint to all that, I thought it would be fun to shed some light on how this little project developed.
It didn’t take very long to generate the basic tree shape with the chart. A loop creates the nodes and links we need. For each of the n layers of the tree:
Running a Sequential layout on this data will naturally produce a chart which looks suspiciously like Christmas tree. Any eyLines fans out there – no doubt already scrambling for your IDEs – should note that you have to set spacing to ‘equal’ to get the right effect.
We also add a final layer, containing a single square shaped node, forming the base of the tree, and a couple of nice thick links for the trunk.
I also added a little randomization to node (and later link) colors to add some texture.
Our tree already looks pretty authentic – but it’s not quite Christmas. We need a little decoration!
Everyone loves tinsel. Sequential layout comes with the ability to loop or “offset” non-adjacent links at the same level – this prevents links from crossing over each other. So to add tinsel, I ruthlessly exploit this feature by adding purplish-red links between non-adjacent nodes.
Because I don’t want to end up on Santa’s naughty list, I have to confess that I cheated a bit here. Usually, the Sequential layout decides whether to loop above or below the intermediate nodes, depending on the available space. In this layout, that results in strange bits of vertical tinsel hovering spookily above the branches. Because tinsel usually hangs downwards (in my house, at least, where we’re big fans of Newtonian physics) I added an option to the layout algorithm to force all non-adjacent same-level links to have a negative offset.
Next, some fairy lights. These are implemented with KeyLines glyphs, shaded with a random-ish shade of yellow to either the north-east or north-west of each node. To make them blink, we need a little bit of real Christmas magic by taking advantage of some experimental, unreleased code which allows us animate properties on glyphs. Using this secret code, we set up a loop which every second or so iterates over each light and decides whether to fade its opacity up or down. We keep this loop in sync with the animation so that lights have time to cycle their brightness before changing again – giving us a nice, gentle blinking effect.
As a final touch, each node has a little bit of snow nestled on top of it. KeyLines has a feature called Donuts, which, as well as sounding delicious, allows us to adds bands of color around a node. I set a white band to appear around the first and last fifth of the node (because donuts start at the top-centre, and a transparent band around the remaining three-fifths. Well, more or less: I add a little randomization to the ratios and thickness. For realism, you understand. Accuracy is paramount here.
To make sure you can actually see all this stuff, I’ve set a dark background on the chart and hidden the navigation controls.
So we have a highly credible, seasonally decorated, suitably festive singleton arboretum. But what of atmosphere, you cry? Where is the romance of crystallized precipitation, the wonder of rolling hills?
Well, the hills are nice and easy. First we set a gradient background on the chart, lightening about 3/4 of the way down the page to give us a nice horizon. Then we add hills as shapes, with a large width and height scaled to, and offset against, the viewport. Hills at the back are given a slightly darker, bluer colour to help give a sense of distance.
And then the snow. Actually this wasn’t nearly as difficult as I thought it would be, at least once I’d got my positioning maths right (which admittedly took two coffees and a mince pie).
Essentially we create an animation loop which runs every 10 seconds or so. Each loop creates a random number of “snowflakes” (nodes) at the top of the screen, each with an offset x/y position so we get a nice natural spread. Then each snowflake is animated to move down the screen by y pixels, and with a slight x offset (both bigger for larger snowflakes) so that they drift pleasantly.
To preserve memory we make sure to remove any nodes from the chart once their y position exceeds a certain point – I picked a point about four-fifths of the way down the screen, so that snowflakes can disappear somewhere amid the snowy hills. There is a very small chance that any snowflake can disappear at any time – just like real snow!
As an extra detail, snowflakes which are about to vanish shrink their size down to zero through one loop, then set a flag to be removed in the next loop. It’s actually quite hard to notice this – but I did find that if a snowflake suddenly vanished, you really did notice – so I’m happy with this little detail.
Finally, a technical note on animation. There is a lot of drawing work going on in this christmassy chart: from the blinking lights of node glyphs, to the hundreds of gently falling (and occasionally shrinking) snowflake nodes.
Originally this ran through two separate animation loops, controlled with a recursive animateProperties call* and a slightly offset from each other. One loop would control the lights, the other the snow. But this resulted in animations blocking each other, causing the snowfall to hang alarmingly in the air.
So instead, I built my own animation queue into the page. Each object that I would pass into animateProperties, I push into an array instead. Then, inside a setInterval loop, every ten seconds or so I pass that animation queue into animateProperties. Having just the one animation thread running in the page helps prevent interrupts, and always keeps the animation stack busy.
And that’s it! A delightful Christmas card written in KeyLines. I am sorry that there are no gifts under the tree – maybe next year?
* Actually that’s not really true – if you run animateProperties recursively, there tends to be a slight but visible delay while the new animations are processed and. So instead, I run a setInterval slightly less than the animate properties duration to prep the next animation slightly before the next lands.
All that’s left to say is to wish you a very Merry Christmas from everyone at Cambridge Intelligence! We look forward to working with you in 2018.