0

# Some polish This is part 9 of an N part series detailing how I make my animations.

I have discovered that there is a command in Processing to plot a point instead of drawing a small ellipse like I have done! I had looked to see how Processing sets pixels which it does with the `set()` command but the pixels were too small. Processing also has a `point()` command which I disregarded as it looked the same. The difference is that `point()` plots a circle using `strokeWeight()` as the size!

`strokeWeight(10);stroke(255,255,0);point(50,50);`

Produces:

Woohoo, now back to our regularly scheduled program…

My maths teacher drummed into my head “always label your axes” so let’s do that along with the actual axes themselves.

Oh, but our scale is continuously changing. This makes it trickier but not impossible…

First we had better make some room for them, currently our plot is shown hard up against the edges of the window.

I was going to just create a `margin` variable and then simply add it to the `x` and `y` values but that turned out to be trickier than I thought as I then had to modify the scale algorithms in a non intuitive manner (what actually happened is that I failed to scale it correctly at all and gave up in disgust).

So a far simpler way is to pull out the code into a function and pass in the new position and the width and height. Sounds like a lot of work but it isn’t. First rename the `draw()` function to `drawGraph()`. Create a new `draw()` function and put in the body `drawGraph();` like so:

`void draw(){   drawGraph();}`
`void drawGraph(){   ...}`

Now add `xPos`, `yPos`, `graphWidth`, `graphHeight` integers to `drawGraph()`.

`void drawGraph(int xPos, int yPos, int graphWidth, int graphHeight)`

I think that a margin of 60 pixels is a good starting point. So let’s make a global constant with that value. Add this line just before `draw()`:

`final int MARGIN = 60;`

Marking something as `final` means it cannot be modified and by convention these variables are named using all caps.

Now all we do is to call `drawGraph()` from `draw()` with the following:

`drawGraph(MARGIN, height - MARGIN, width - MARGIN, height - MARGIN);`

So let’s start using those values in `drawGraph()`:

First replace all uses of `width` and `height` with `graphWidth` and `graphHeight` respectively.

Then replace the `ellipse()` function with the newly discovered `point()` function:

`point(xPos + x * xScale, yPos - (y * yScale));`

And you’re done. Drawing with `point()` also seems a lot faster! Oh yeah, add `strokeWeight(3)` to `setup()` otherwise your points will be 1 pixel in size.

Ok, let’s draw some axes. Simple lines from top to bottom using `MARGIN` so in the `draw()` function:

`line(MARGIN, 0, MARGIN, height);line(0, height - MARGIN, width, height - MARGIN);`

Hmm, that didn’t work first time for me. This was due to leaving `background(0)` in the `drawGraph()` function which was clearing the drawn lines. Move that to the first line of `draw()` and it will all work.

Result should look like this:

The line thickness is inherited from the `strokeWeight` command in `setup()`. So let’s move that to the first line of `drawGraph()` and add `strokeWeight(1)` to the first line of `draw()`. That will make a thin line for the axes and heavy points for the graph.

So now we need to add tick marks. As the tick marks need to be scaled to the same as the graph then they’ll need to be drawn from inside the `drawGraph()` function. Let’s do the x axis first by marking each year.

Yikes. This isn’t as easy as I thought it would be. Because each data point is recorded on a Saturday and we are simply multiplying our `dataIndex` by 7 to get the number of days from the start date to find the date to get the data it makes working out the tick marks non trivial.

After a fair bit of experimentation I was able to reduce the complexity to the following function:

`void drawXAxis(int xAxisMaximum, float xScale){  int yPos = height - MARGIN;  float xAxisTick = 279.0 / 7.0;  int axisYear = 1958;    while(xAxisTick <= xAxisMaximum)  {    int tickLength = 5;    int xPos = MARGIN + int(xAxisTick * xScale);        if(axisYear % 5 == 0)    {      text(axisYear, xPos, yPos + 22);      tickLength = 10;    }            line(xPos, yPos, xPos, yPos + tickLength);        xAxisTick += 365.25 / 7.0;    axisYear++;  }}`

Where the `xAxisMaximum` starts off as the width of the plot and then as the scale increases the scaled max x value. The initial value of the `xAxisTick` is the number of days left in the year after 29/3/1958 (279) converted to weeks by dividing by 7.

Here I am introducing a `while` loop. This type of loop keeps executing while the contents of the parenthesis is true. In this case while the current position of the tick is less than the maximum x value on the axis.

The line `if(axisYear % 5 == 0)` makes use of the modulus operator `%` to make a longer tick mark and display the year every fifth year. We then simply draw a line for the tick followed by incrementing the tick position by the average number of weeks in a year.

This results in:

Which I’m rather pleased with. Here’s the complete 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);  textAlign(CENTER);    Ani.init(this);  Ani.setDefaultTimeMode(Ani.FRAMES);  _ani = new Ani(this, 1032, "_change", 3, Ani.EXPO_IN);}`
`float yScale = 20.0;`
`boolean _coda = false;int _codaCount = 0;`
`float _change = 1.0;final int MARGIN = 60;`
`void draw(){  background(0);  strokeWeight(1);    line(MARGIN, 0, MARGIN, height);  line(0, height - MARGIN, width, height - MARGIN);    drawGraph(MARGIN, height - MARGIN, width - MARGIN, height - MARGIN);}`
`void drawGraph(int xPos, int yPos, int graphWidth, int graphHeight){  int deltaX = int(frameCount * _change);      float xScale = 1.0;    int xAxisMaximum = graphWidth;    if(deltaX > graphWidth)  {    xAxisMaximum = deltaX;    xScale = float(graphWidth) / float(deltaX);  }    drawXAxis(xAxisMaximum, xScale);    strokeWeight(3);    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 > graphHeight)       {        yScale = float(graphHeight)/y;      }            point(xPos + x * xScale, yPos - (y * yScale));    }  }    }`
`void drawXAxis(int xAxisMaximum, float xScale){  int yPos = height - MARGIN;  float xAxisTick = 279.0 / 7.0;  int axisYear = 1958;    while(xAxisTick <= xAxisMaximum)  {    int tickLength = 5;    int xPos = MARGIN + int(xAxisTick * xScale);        if(axisYear % 5 == 0)    {      text(axisYear, xPos, yPos + 22);      tickLength = 10;    }            line(xPos, yPos, xPos, yPos + tickLength);        xAxisTick += 365.25 / 7.0;    axisYear++;  }}`
`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;    float co2 = parseFloat(values);    _data.set(date, co2);  }}`

That was a whole lot more complicated than I was expecting. I intended this post to cover both the x and y axis along with titles etc! I guess that’ll be next week then…                #### Tags Subscribe to get your daily round-up of top tech stories!