Image maps of an arbitrary area
Sat, 14 Aug 2021 13:23:56 +0100

Introduction

Web developers may be familiar with the map and area tags to define clickable areas in an image. The area tag lets you define rectangular, circular, or polygonal areas. But wouldn't it be easy if we just painted the areas in a separate image that defines the clickable sections? That's what I made this tool for: Dumper (github).

It was common in the old 8-bits and 16-bits days to use indexed images this way to define areas. However, indexed images aren't a thing anymore, and the common thing in browsers is that when the image gets loaded, it gets automatically converted to an RGB image. That means it's not easy to get the index values with standard Javascript. So this Dumper web app I made simply lets you define a color palette, and looks for the actual RGB values in your images to give you the index back from the color palette. You can dump the values as hexadecimal numbers. The numbers are displayed with their actual color in the palette. The idea is that you copy and paste those values in your source code to embed image selections.

You can try the Dumper web app here: Dumper (web).

I made this tool last year because I wanted to make a map-based selection screen for my latest game, Sopilabitas (Word Wall ES), a Spanish word puzzle game. Here are a couple of those selection screens, as the user taps at different sections on the map:

Image maps in Swift

The way I use these maps in the game, which it's programmed in Swift, is through these data types:

typealias UVCoord = (Float, Float)

struct ImageSegment {
    let name: String
    let centroid: UVCoord
}

struct ImageSegmentation {
    static let empty = ImageSegmentation(width: 1, height: 1, segments: [], data: [0])
    let width: Int
    let height: Int
    let segments: [ImageSegment]
    let data: [Int]
    func get(uv: UVCoord) -> Int {
        let x = Clamp(Int(uv.0 * Float(width)), low: 0, high: width - 1)
        let y = Clamp(Int((1 - uv.1) * Float(height)), low: 0, high: height - 1)
        let i = y * width + x
        return data[i]
    }
}  
So the data for each map looks like this:

let southAmerica = ImageSegmentation(
    width: 24,
    height: 52,
    segments: [
        ImageSegment(name: "Chile", centroid: (0.25, 0.307692)),
        ImageSegment(name: "Argentina", centroid: (0.375, 0.288462)),
        // ...
    ],
    data: [0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 
      //...
    ]
)
The get(uv) function in ImageSegmentation is used to convert the tapped coordinates to an index value from the map, and that way we know which country was tapped. It's that simple.

Indices can also be used in other places, such as defining collision areas in games. It's a very simple way to do collisions, but it works for 2D games. Perhaps you can find other usages.

I hope this is helpful to anyone making these kind of region-based interactive screens, or any other game or app.


◀️ Older | Newer ▶️

⏪ Previous year | Next year ⏩