Pay attention in math class, kids
Overview
I never thought when I was in my high school geometry or pre-calculus class that I’d ever really use the things I learned. Quadratic equations… matrices… formulas for calculating lengths of triangles sides and degrees of their angles, who needs that?
Apparently I did.
Fast forward a few years. I’m here at Interactive Knowledge and our latest client was an Alaskan cultural heritage institution that needed several touch-screen interactives for an exhibit that they were opening.
One of the interactives is a demonstration on the history and unique method of Halibut fishing that has been passed down for generations as part of the native cultures in Alaska. The lineup game requires the player to move their boat in line with 2 landmarks to triangulate a location where the fish are. This requires the game piece (a boat) to rotate and move across the screen, get in line with 2 landmarks, and verify the player’s success. Sounds simple enough, right?
Left, right, up and down are easy to do using a little CSS and Javascript. But after you find the first lineup, the boat needs to travel along the line to find and line up with the other landmarks.
I needed to figure out how not just to move up, down, left and right but diagonally along a line at a specific angle. Here is where geometry was required.
While I won’t disclose the exact length of time it's been since my said high school days it has been long enough that I did not remember how to calculate a side or angle of a triangle. Jonny and Margaret Hamilton who published this gem that helped me to figure out the answers to my calculations.
Exhibit 1: How to Calculate
I have several other pages that are filled with calculations trying to determine if I want to move the game piece 60 pixels diagonally (the hypotenuse), when starting out at a point (CSS top and left attributes, or x, and y coordinates) and I know that my angle is 66 degrees to the north west… what are my positioning (CSS top and left attributes) to move the actual game piece? (Finding the adjacent and the opposite sides) I was able to use the equations to calculate how to move the boat in the game and determine where the shooter line goes.
Exhibit 2: Example calculations
To find the next coordinates if the player moves the boat, I add the right position (300px) to the adjacent side length to get the final right coordinate and subtract the opposite side length with the top (290px) position.
Opposite Side = sin(angle) x hypotenuse
Opposite Side = sin(66°) x 60
Opposite Side = 54
Adjacent Side = cos(angle) x hypotenuse
Adjacent Side = cos(66°) x 60
Adjacent Side = 24
If our starting boat element css looks like this:
#boat {
right: 300px;
top: 290px;
}
Then our ending css would look like this:
#boat {
right: 324px;
top: 234px;
}
Exhibit 3: Land Collisions
Another bonus challenge to all this: if you play the game, you’ll notice the boat never drives over the land. Of course, we could have just said the boat could drive all over the board and look like it was driving on land, but what fun would that be? Behind the image, I have canvas draw the outline of the landscape in white and green. (See source image example below)
As the boat moves the app calculates the next possible movements as shown above and gets the pixels on the canvas with the landscape outline that matches. If the pixel of the next movement is green, then the movement will be disabled. If the pixel is white it means the boat can move to that position.
/**
* Loads the outline image and draws it on the canvas.
*/
_loadImage = () => {
const image = new Image()
const context = this.canvas.getContext('2d')
context.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
image.onload = () => {
context.drawImage(image, 0, 0, this.canvasWidth, this.canvasHeight)
}
image.src =
this.props.map === 1
? canvasImg1
: this.props.map === 2
? canvasImg2
: canvasImg3
image.width = this.canvasWidth
image.height = this.canvasHeight
}
/**
* Helper method for getting pixel location from a canvas element.
* @param {integer} x x coordinate
* @param {integer} y y coordinate
* @return {string} calculated (numerical) hexcode for the coordinates given on the canvas element.
*/
_getPixel = (x, y) => {
const canvas = document.getElementById('map-layer') // This is the canvas HTML element
const context = canvas.getContext('2d')
const imageData = context.getImageData(x - 1, y - 1, 1, 1) // Returns all the pixel at the x and y coordinates
const d = imageData.data
const rgb = ((d[0] << 16) | (d[1] << 8) | d[2]).toString(16) // Convert the data to RGB.
return ('000000' + rgb).slice(-6) // Convert RGB to Hexcode
}
/**
* Method to determine if given coordinates is a land coolision.
* @param {integer} x [description]
* @param {integer} y [description]
* @return {Boolean} true is "yes land collision"
*/
_isLandCollision = (x, y) => {
// In the game, determine what all the possible next movements are, feed into this method
// Then will return if the game piece is allowed to move to that spot.
// If this returns true, it disables the ability to move that direction.
if (this.state.stage === 2) {
x = parseInt(x, 10)
y = parseInt(y, 10)
}
const color = this._getPixel(x, y)
return color !== 'ffffff'
}
Since the beginning of my career in programming, it’s been apparent (and surprising) how much math and problem solving is required. So, kids, the moral of the story is: if you want to be a programmer, pay attention in math class. You never know when you’ll need it!
Exhibit 4: Play the game!
Take a minute to play the game! Since the interactive was built for a kiosk, it is not responsive and is best to view on desktop.
View the source code on Github
Read more about Halibut, Attach the Hook! and the other interactives created for the Sealaska Heritage Institute Our Grandparents’ Names on the Land exhibit.