Let's go in to some detail about the approach taken to draw the hexagram. This is a handy little trick that maybe you can reuse in one of your projects. It uses a very simple and tiny sprite sheet Bitmap to source the two basic line symbols used to generate the hexagram. These are then used in a TileGrid to generate the actual hexagram. To render this large on the display, the displayio Group's scale feature is used.

Sprite Sheet

For a more in depth overview of what a sprite sheet is, see this section of the displayio guide:

Look again at the two lines that comprise the hexagram. They are very basic - just a line and dashed line. We can represent these with a simple bitmap like this:

This bitmap is only 11 pixels wide by 4 pixels high. Each blue square is a pixel. The two blank lines are there for padding. The width was chosen for basic aesthetic reasons, generating a nice overall aspect ratio for the lines. This bitmap is so simple that it is generated programmatically. That's what these lines of code are doing:

for x in range(11):
    sprite_sheet[x, 0] = 1 # - - 0 YIN
    sprite_sheet[x, 1] = 0
    sprite_sheet[x, 2] = 1 # --- 1 YANG
    sprite_sheet[x, 3] = 0
sprite_sheet[5, 0] = 0

Using a Tile Grid for the Hexagram

The sprite sheet can then be used along with a TileGrid to generate any of the 64 hexagrams. The TileGrid will be 1 column by 6 rows - one row for each line of the hexagram. Each cell is 11 pixels wide by 2 pixels high, corresponding to one of the symbols from the sprite sheet.

Then it's a simple matter of setting the source index for each cell of the TileGrid to point to one of the two available bitmaps from the sprite sheet. For example, to make the 3rd line down a dashed (Yin) symbol, we would do something like this:

To generate the entire hexagram we just set each cell in the TileGrid to a source from the sprite sheet. Like this:

Some thought has been put into the arrangement of the sprite sheet. By making the dashed (Yin) symbol be first, we can then generate a hexagram from any integer 0 to 63 (64 total) by simply using each of its bits to set the TileGrid's sources.

In the example above, that would be 0b100101 = 37. Remember, the least significant bit is on the bottom.

That general approach is what is being down in the show_hexagram function:

def show_hexagram(number):
    for i in range(6):
        tile_grid[5-i] = (number >> i) & 0x01

Using Scale to Embiggen

In its raw form, the sprite sheet is only 11 pixels wide by 4 pixels high. The hexgram created from the sprite sheet ends up being 11 pixels wide by 12 pixels high. That's pretty small. So how can we make it bigger on the display? By using scale.

The scale parameter is an option for the displayio.Group class. It is used as a multiplier when rendering the contents of the Group. Every pixel is drawn "scale" number of times. So if scale=3, then every 1 pixel would become 3x3 pixels on the display.

A scale factor of 10 is used for the hexagram, which is specified in this line of code:

hexagram = displayio.Group(max_size=1, x=60, y=15, scale=10)

So the 11x12 hexagram ends up being drawn at an actual size of 110x120 pixels.

There's no fancy smoothing done, which can lead to a very coarse look to the scaled up bitmap. However, since our shapes are blocky rectangles, this works out just fine.

This guide was first published on Apr 27, 2020. It was last updated on 2023-12-05 11:34:49 -0500.

This page (Drawing the Hexagram) was last updated on Apr 01, 2020.

Text editor powered by tinymce.