Kevin Pluck

@kevpluck

Rates of change

This is part 8 of an N part series detailing how I make my animations.

Prev Next

We don’t want to bore our audience with a long slow animation so let’s speed things up a bit. Currently we are drawing one dot per frame so the simplest way to go is multiple dots per frame by creating a variable called, say, deltaX and setting it to five times the frame count:

int deltaX = frameCount * 5;

Then replace all references of frameCount to deltaX resulting in:

Just like that. Here’s the whole code:

import java.time.*;
FloatDict _data = new FloatDict();
LocalDate _startDate = LocalDate.of(1958, 3, 29);
void setup()
{
loadData();
size(500,500);
background(0);
stroke(255,255,0);
}
float yScale = 20.0;
void draw()
{
background(0);
float xScale = 1.0;

int deltaX = frameCount * 5;

if(deltaX > width)
{
xScale = float(width) / float(deltaX);
}
  for(int dataIndex = 1; dataIndex <= deltaX; dataIndex++)
{
int daysFromStart = (dataIndex - 1) * 7;
LocalDate frameDate = _startDate.plusDays(daysFromStart);

if(_data.hasKey(frameDate.toString()))
{
float co2 = _data.get(frameDate.toString());

float x = dataIndex;
float y = (co2 - 313.04) ;

if(y * yScale > height)
{
yScale = float(height)/y;
}

ellipse(x * xScale, height - (y * yScale), 1, 1);
}
}
}
void loadData()
{
String[] lines = loadStrings("weekly_in_situ_co2_mlo.csv");

for (String line : lines)
{
if( line.startsWith("\"") ) continue;

String[] values = split(line, ',');
String date = values[0];
float co2 = parseFloat(values[1]);
_data.set(date, co2);
}
}

But, I feel it’s too fast at the start yet not fast enough at the end. This means we should vary the rate during the animation. One approach would be to use the frameCount to vary the rate but this gets complicated very quickly.

This sort of thing is very common in animations and conveniently clever types have made libraries that simplify this. These libraries are called “easing libraries” and we will be using one called Ani written by Benedikt Groß.

To install Ani go to Processing’s Tools menu and select Add Tool... In the Libraries tab search for Ani and click install.

What this library does is to change the value of a particular variable over the course of an animation using a chosen algorithm. We would like to change the speed of the animation from slow to fast in a steady fashion. To do this we replace the constant value 5 in the code above with a variable. I’m calling this variable _change and setting its initial value to 1.0.

float _change = 1.0;
void draw()
{
int deltaX = int(frameCount * _change);

Clearly this doesn’t do much by itself.

Now let’s activate Ani. First we import it:

import de.looksgood.ani.*;
Ani _ani;

_ani is the variable that will be containing the Ani object.

Then we set it up in setup():

  Ani.init(this);
Ani.setDefaultTimeMode(Ani.FRAMES);
_ani = new Ani(this, 1032, "_change", 3.0, Ani.EXPO_IN);

this is a reference to our Keeling animation letting the Ani library know where to find things. By default the TimeMode is based on seconds but I find it to be more accurate to use frames as we can be dealing with many thousands of data points and each frame can take a while to render.

So the meat and potatoes of the Ani library is contained in the line:

_ani = new Ani(this, 1032, "_change", 3.0, Ani.EXPO_IN);

What this is saying is over 1,032 frames¹ modify the value in the _change variable to end up with the value 3 exponentially. Remember we defined _change to start with the value 1.0 above so this slowly increases this value until it reaches 3.0 after 1,032 frames.

The full documentation is available here.

Running this we get:

As you can see it starts of nice, slow and easy then rapidly increases in speed.

Here is the full code:

import java.time.*;
import de.looksgood.ani.*;
Ani _ani;
FloatDict _data = new FloatDict();
LocalDate _startDate = LocalDate.of(1958, 3, 29);
void setup()
{
loadData();
size(500,500);
background(0);
stroke(255,255,0);
frameRate(30);

Ani.init(this);
Ani.setDefaultTimeMode(Ani.FRAMES);
_ani = new Ani(this, 1032, "_change", 3.0, Ani.EXPO_IN);
}
float yScale = 20.0;
float _change = 1.0;
void draw()
{
int deltaX = int(frameCount * _change);

background(0);
float xScale = 1.0;

if(deltaX > width)
{
xScale = float(width) / float(deltaX);
}

for(int dataIndex = 1; dataIndex <= deltaX; dataIndex++)
{

int daysFromStart = (dataIndex - 1) * 7;
LocalDate frameDate = _startDate.plusDays(daysFromStart);

if(_data.hasKey(frameDate.toString()))
{
float co2 = _data.get(frameDate.toString());

float x = dataIndex;
float y = (co2 - 313.04) ;

if(y * yScale > height)
{
yScale = float(height)/y;
}

ellipse(x * xScale, height - (y * yScale), 1, 1);
}
}
}
void loadData()
{
String[] lines = loadStrings("weekly_in_situ_co2_mlo.csv");

for (String line : lines)
{
if( line.startsWith("\"") ) continue;

String[] values = split(line, ',');
String date = values[0];
float co2 = parseFloat(values[1]);
_data.set(date, co2);
}
}

Next week we’ll be adding text and a bit of polish!

¹ I came up with 1,032 through trial and error. I’m sure there was a way of calculating it but life’s too short.

Hacker Noon is how hackers start their afternoons. We’re a part of the @AMIfamily. We are now accepting submissions and happy to discuss advertising & sponsorship opportunities.
To learn more, read our about page, like/message us on Facebook, or simply, tweet/DM @HackerNoon.
If you enjoyed this story, we recommend reading our latest tech stories and trending tech stories. Until next time, don’t take the realities of the world for granted!

More by Kevin Pluck

Topics of interest

More Related Stories