Code a grid of squares in Pts.js

Date: 2023-08-13 | create | tech | pts | typescript |

I recently released TimeTick - a visualization of the passing of time. TimeTick's Time page uses a grid to visualize the passing of time in relation to expected time available. This required building a dynamic grid of squares in my visualization library of choice Pts.js.

In this post we'll explore how to build this:

Q: How can we build a dynamic grid of squares in Pts.js?

The solution will be generalized across:

  • Grid elements (you could use a square, circle, etc)
  • Grid sizes

Answer

The solution provided here builds a 4x5 grid of squares and draws it to the space (canvas).

Run this example yourself by copy / pasting the code below into the Pts.js Playground

Solution Overview

In order to produce the grid in the demo above, our pseudo code needs to do something like this:

  • Create all rectangles
  • Figure out their position in the grid and move them there
  • Loop over all rectangles and draw them to the space (canvas)

Grid Code

The core part of our solution is to create a grid of squares. I've isolated it out into its own function to try and make it as easy to understand as possible.

This function creates a grid of squares described as an array of Groups of pts (if this is confusing, Pts' Get Started guide may help). Note that this just creates the data equivalent of these squares - it does not actually draw these to the canvas.

The code below:

  • Creates an array of allRectangles that will be present in the grid
  • Loops over each rectangle and calculates what its position should be, moves it there
  • Returns allRectangles
const buildCenteredGridOfRectangles = (
    space : CanvasSpace,
    rectangleCountPerRow : number,
    rowCount : number,
    rectangleSize : number) : Group[] => {
    const allRectangles = Array(rectangleCountPerRow * rowCount)
        .fill(0)
        .map(i => Rectangle.from(
            space.center,
            rectangleSize
        ))

    for(let i = 0; i < allRectangles.length; i++) 
    {
        const currentRect = allRectangles[i]

        const x = space.center[0] + ((i % rectangleCountPerRow) - (rectangleCountPerRow / 2)) * rectangleSize * 2
        const y = space.center[1] + (Math.floor(i / rectangleCountPerRow) - (rowCount / 2)) * rectangleSize * 2

        currentRect.moveTo(x, y)
    }

    return allRectangles
}

Full Code

Below is the full sketch used to validate this approach. I recommend copy / pasting into the Pts.js Playground and playing around with the code.

Note: The playground doesn't seem to support TypeScript so the grid code here is the same as above, just with types stripped out.

(function() {

  // Pts quick start mode.
  var run = Pts.quickStart( "#pt", "#fff" ); 

    const buildCenteredGridOfRectangles = (
        space ,
        rectangleCountPerRow ,
        rowCount ,
        rectangleSize ) => {
        const allRectangles = Array(rectangleCountPerRow * rowCount)
            .fill(0)
            .map(i => Rectangle.from(
                space.center,
                rectangleSize
            ))

        for(let i = 0; i < allRectangles.length; i++) 
        {
            const currentRect = allRectangles[i]

            const x = space.center[0] + ((i % rectangleCountPerRow) - (rectangleCountPerRow / 2)) * rectangleSize * 2
            const y = space.center[1] + (Math.floor(i / rectangleCountPerRow) - (rowCount / 2)) * rectangleSize * 2

            currentRect.moveTo(x, y)
        }

        return allRectangles
    }

  run( (time, ftime) => {

    const allRectangles = buildCenteredGridOfRectangles(
        space,
        4,
        5,
        10
    )

    allRectangles
        .forEach(r => {
            form 
                .fill("#fff")
                .stroke("#000")
                .polygon(
                    Rectangle.corners(r)
                )
        })

  });

})();

Next Steps

If you liked this, you might be interested in some related reads:

Want more like this?

The best / easiest way to support my work is by subscribing for future updates and sharing with your network.