Hexagonal Grids

This section assumes you are very familiar with the concepts, terms and ideas for protograf as presented in the Basic Concepts , that you understand all of the Additional Concepts and that you’ve created some basic scripts of your own using the Core Shapes.

Overview

Hexagonal grids are now widely used in the table top gaming industry.

They are particularly suitable in providing an overlay for maps and have been used for decades in war games and role playing games, but can also act as grids or tiles in regular board games.

Some practical examples of these grids are shown in the section with both commercial and abstract boards.

You should have already seen how a single Hexagon and a basic grid of Hexagons are created using defaults. You should also have seen how a single hexagon can be further enhanced as a Customised Hexagon Shape.

Rectangular Hexagonal Grid

The basic hexagonal grid is laid out in a rectangular fashion. It can be customised in a number of ways.

Rows and Columns

^

rr1

This example shows a grid constructed using the command:

Hexagons(
    side=0.5,
    x=1, y=1,
    rows=3, cols=3
)

It has the following properties that differ from the defaults:

  • side - length of each side of a hexagon in the grid

  • x and y are used to set the upper-left corner of the grid

  • rows - number of rows in the grid

  • cols - number of columns in the grid

rr2

This example shows a grid constructed using the command:

Hexagons(
    side=0.5,
    x=1, y=1,
    rows=3, cols=3,
    orientation="pointy"
)

It has the following properties that differ from the defaults:

  • side - length of each side of a hexagon in the grid

  • x and y are used to set the upper-left corner of the grid

  • rows - number of rows in the grid

  • cols - number of columns in the grid

  • orientation set to``pointy``, ensuring there is a “peak” for each hexagon

Coordinates

^

Every location in a grid has a row and column number — these are not, by default, displayed on the grid; but they are needed in some cases; for example, to support grid references for a wargame map.

The coordinate system starts at the top-left of the grid; the column is, by default, the first value (the “x” location) and the row is the second value (the “y” location).

The coordinates can be displayed using either letters (upper or lowercase) or numbers (the default behaviour). A separator may be specified to help visualise, or differentiate, the row versus the column value. For numeric coordinates, numbers have a “zero padding”; so 1 is displayed as 01.

The coordinates can also be displayed in various positions within the hexagon.

Most coordinate property names are prefixed with coord_.

rc1

This example shows grids constructed using the commands:

Hexagons(
    side=0.6,
    x=0, y=0,
    rows=2, cols=2,
    coord_elevation="middle",
    coord_prefix='z',
    coord_suffix='!',
)
Hexagons(
    side=0.6,
    x=2, y=3,
    rows=2, cols=2,
    fill="darkseagreen",
    coord_elevation="top",
    coord_type_x="upper",
    coord_separator='::',
)

Each has the following properties that differ from the defaults:

  • side - length of each side of a hexagon in the grid

  • x and y are used to set the upper-left corner of the grid

  • rows - number of rows in the grid

  • cols - number of columns in the grid

  • coord_elevation can be top, middle or bottom to set the vertical position of the coordinates text; the horizontal always matches to the hexagon’s centre

The white grid also has:

  • coord_prefix - this is text that appears before the row and column values are shown (to their left)

  • coord_suffix - this is text that appears after the row and column values are shown (to their right)

The green grid also has:

  • coord_type_x - upper displays the column (x-value) as an uppercase letter

  • coord_separator - can be any text used that must be displayed between the row and column values; in this case it is two colons ::

rc2

This example shows grids constructed using the commands:

Hexagons(
    side=0.6,
    x=0, y=1,
    rows=2, cols=2,
    orientation="pointy",
    fill="white",
    coord_elevation="middle",
    coord_prefix='z',
    coord_suffix='!',
)
Hexagons(
    side=0.6,
    x=1, y=4,
    rows=2, cols=2,
    orientation="pointy",
    fill="darkseagreen",
    coord_elevation="top",
    coord_type_x="upper",
    coord_separator='::',
)

Each has the following properties that differ from the defaults:

  • side - length of each side of a hexagon in the grid

  • x and y are used to set the upper-left corner of the grid

  • rows - number of rows in the grid

  • cols - number of columns in the grid

  • orientation set to pointy to have hexagons with pointed tops

  • coord_elevation can be top, middle or bottom to set the vertical position of the coordinate text

The white grid also has:

  • coord_prefix - this is text that appears before the row and column values are shown (to their left)

  • coord_suffix - this is text that appears after the row and column values are shown (to their right)

The green grid also has:

  • coord_type_x - upper displays the column (x-value) as an uppercase letter

  • coord_separator - can be any text used that must be displayed between the row and column values; in this case it is two colons ::

Caltrops

^

Caltrops is a term when the point at which three hexagons meet is drawn by a set of three small lines; these replace the normal edge of the hexagon.

rp1

This example shows a grid constructed using the command:

Hexagons(
    side=0.6,
    x=0, y=1,
    rows=3, cols=3,
    dot=0.04,
    caltrops=0.15,
)

It has the following properties that differ from the defaults:

  • side - length of each side of a hexagon in the grid

  • x and y are used to set the upper-left corner of the grid

  • rows - number of rows in the grid

  • cols - number of columns in the grid

  • dot draws a small dot (of size 0.04) in the centre of a hexagon

  • caltrops - length of the caltrop lines

rp2

This example shows a grid constructed using the command:

Hexagons(
    side=0.6,
    x=0, y=1,
    rows=3, cols=3,
    orientation="pointy",
    dot=0.04,
    caltrops=0.2,
    caltrops_invert=True,
)

It has the following properties that differ from the defaults:

  • side - length of each side of a hexagon in the grid

  • x and y are used to set the upper-left corner of the grid

  • rows - number of rows in the grid

  • cols - number of columns in the grid

  • orientation set to pointy to have hexagons with pointed tops

  • dot draws a small dot (of size 0.04) in the centre of the hexagon

  • caltrops - size of the caltrop lines

  • caltrops_invert - if set to True, this will cause the caltrops line to be drawn in the middle between the hexagon vertices; with no lines drawn touching those vertices

Hidden

^

As every location in a grid has a row and column number, these values can be used to hide or mask certain hexagons from being displayed. This can be useful when a grid is designed for a scenario where not all hexagons are needed.

rdd

This example shows grids constructed using the commands:

Hexagons(
    side=0.5,
    x=0, y=0,
    rows=3, cols=3,
    fill="white",
    hidden="2,1 2,3"
)
Hexagons(
    side=0.5,
    x=1, y=3,
    rows=3, cols=3,
    fill="darkseagreen",
    orientation="pointy",
    hidden=[(1, 2), (1, 3), (3, 2), (3, 3)]
)

Each has the following properties that differ from the defaults:

  • x and y are used to set the upper-left corner of the grid

  • rows - number of rows in the grid

  • cols - number of columns in the grid

In the white flat grid:

  • hidden - a string of row and column numbers

The pairs of comma-delimited row and column numbers are each separated by a space.

In this example, the second row hexagon is hidden in both first and second columns.

In the green pointy grid:

  • hidden - a list ([ to ]) of row and column numbers

The row and column numbers are in the form of one or more sets; with each pair enclosed by round brackets.

In this example, the second and third columns are hidden in both the first and the third row.

Offset

^

rof

This example shows grids constructed using the commands:

Hexagons(
    side=0.5,
    x=0, y=0.5,
    rows=3, cols=3,
    hex_offset="even",
    coord_elevation="middle",
    coord_font_size=5,
    coord_separator=' r',
    coord_prefix='c',
)
Hexagons(
    side=0.5,
    x=1, y=3.5,
    rows=3, cols=3,
    hex_offset="even",
    orientation="pointy",
    fill="darkseagreen",
    coord_elevation="middle",
    coord_font_size=5,
    coord_separator=' r',
    coord_prefix='c',
)

Each has the following properties that differ from the defaults:

  • side - length of each side of a hexagon in the grid

  • x and y are used to set the upper-left corner of the grid

  • rows - number of rows in the grid

  • cols - number of columns in the grid

  • hex_offset - if even, then every even column — for a flat grid — or every even row — for a pointy grid — is offset by one-half hexagon from those on either side

  • coord_… - various settings to control the appearance of the hex coordinates

Radii

^

rdi

This example shows grids constructed using the commands:

Hexagons(
    side=0.5,
    x=0.5, y=0,
    rows=3, cols=3,
    hex_offset="odd",
    radii="w ne se",
)
Hexagons(
    side=0.5,
    x=1.25, y=3,
    rows=3, cols=3,
    stroke="red",
    radii_stroke="red",
    hex_offset="even",
    radii="e nw sw",
)

Each has the following properties that differ from the defaults:

  • side - length of each side of a hexagon in the grid

  • x and y are used to set the upper-left corner of the grid

  • rows - number of rows in the grid

  • cols - number of columns in the grid

  • hex_offset determines which columns are shifted (odd for the black grid and even for the red grid)

  • radii - as described for a customised hexagon, this will create lines running from each hexagon centre to the vertices, as define by the directions specified

Circular Hexagonal Grid

An alternative to the basic hexagonal grid, is a circular, or circle, layout.

Thes are sometimes termed “hexhex” grids.

Most of the properties that associated with the basic grid are can also be used for the circular grid: coordinates; caltrops; radii and hidden hexagons.

Basic

^

cbs

This example shows a grid constructed using the command:

Hexagons(
    x=0.25, y=1,
    height=0.75,
    sides=3,
    fill="white",
    hex_layout="circle",
)

It has the following properties that differ from the defaults:

  • x and y are used to set the upper-left corner of the grid

  • height - side-to-side height of a hexagon in the grid

  • sides - number of hexagons running along each “edge” of the grid - there are six sides in all

  • fill - color used for area of each hexagon in the grid

  • hex_layout is set to circle to create the circular effect

Nested Shapes

^

cns

This example shows a grid constructed using the command:

Hexagons(
    x=0.25, y=1,
    height=0.75,
    sides=3,
    stroke=None, fill="white",
    hex_layout="circle",
    centre_shape=hexagon(
        stroke="black",
        fill="lightsteelblue",
        height=0.6, stroke_width=2),
)

It has the following properties that differ from the defaults:

  • x and y - the upper-left corner of the grid

  • height - the side-to-side height of a hexagon in the grid

  • sides - the number of hexagons running along each “edge” of the grid; there are six edges in all

  • fill - color used for area of each hexagon in the grid

  • hex_layout is set to circle to create a circular grid

  • centre_shape - a shape that will appear inside all hexagons

The centre point of the centre_shape aligns with the centre of the hexagon.

The location of the centre_shape will match that of the hexagon within which it is “nested”; in this case its size is smaller — 0.6 is less than 0.75 — so there is a “gap” around each of the shapes.

Diamond Hexagonal Grid

An alternative to the basic hexagonal grid, is a diamond layout.

Most of the properties that associated with the basic grid are can also be used for the diamond grid: coordinates; caltrops; radii and hidden hexagons.

Basic

^

dmb

This example shows a grid constructed using the command:

Hexagons(
    x=0.25, y=1,
    height=0.75,
    rows=3,
    fill="white",
    hex_layout="diamond",
)

It has the following properties that differ from the defaults:

  • x and y are used to set the upper-left corner of the grid

  • height - side-to-side height of a hexagon in the grid

  • row - number of hexagons in each row of the grid

  • fill - color used for area of each hexagon in the grid

  • hex_layout is set to diamond to create the layout pattern

Grid Locations

In order to layout objects within a hexagonal grid, it is possible to use the Location() or Locations() command to specify the “what, where and how”.

These commands should work with any of the types of hexagonal grid layouts described above.

The following are the key properties required for the Location() or the Locations() command:

  • grid - a grid, or the name assigned to a grid

  • coordinates - these are coordinates assigned when creating the grid; if none have been assigned, the default numbering is used i.e. a label made up of two 2-digit numbers (each padded with zero) which correspond to the row and column - bear in mind the numbering starts at the top-left of the grid

  • shapes - a list (using square brackets [ and ]) of one of more shapes, appearing in the order that they must be drawn; the centre of the shapes will be set to match the centre of the hexagon in which its drawn.

All examples below make use of a common property (assigned to the name a_circle) defined as:

a_circle = Common(radius=0.4)

Location

^

Example 1. Single Shape

^

hl0

This example shows a location constructed using the command:

hexgrid = Hexagons(
    side=0.5,
    x=0, y=0,
    rows=6, cols=4,
)
Location(
    hexgrid,
    "0101",
    [circle(common=a_circle)]
)

The Hexagons grid is constructed as per the examples described in the Rectangular Hexagonal Grid section.

The grid is assigned the name hexgrid so it’s result can be reused.

The Location command has the following properties:

  • hexgrid refers to the assigned name for the Hexagons grid

  • “0101” contains the co-ordinate of the top-left hexagon in the grid

  • a list, with a shape

The Location’s list contains just one shape — a Circle which will be drawn at the centre of the hexagon matching the co-ordinate that has been set.

Example 2. Multiple Shapes

^

hl1

This example shows a location constructed using the command:

hexgrid = Hexagons(
    side=0.5,
    x=0, y=0,
    rows=6, cols=4,
)
Location(
    hexgrid,
    "0101",
    [circle(common=a_circle), dot()]
)

The Hexagons grid is constructed as per the examples described in the Rectangular Hexagonal Grid section.

The grid is assigned the name hexgrid so it’s result can be reused.

The Location command has the following properties:

  • hexgrid refers to the assigned name for the Hexagons grid

  • "0101" is the co-ordinate of the top-left hexagon in the grid

  • a list of shapes

The list contains two shapes — a Circle and a Dot; these will be drawn in that order, each at the centre of the hexagon matching the co-ordinate that has been set.

Locations

^

It is often the case that the same shape, or set of shapes, needs to be displayed at multiple locations within the grid.

Example 1. Locations and Shapes

^

ml0

This example shows locations constructed using the command:

hexgrid = Hexagons(
    side=0.5,
    x=0, y=0,
    rows=6, cols=4,
)
Locations(
    hexgrid,
    "0204, 0101",
    [circle(common=a_circle), dot()]
)

The Hexagons grid is constructed as per the examples described in the Rectangular Hexagonal Grid section.

The grid is assigned the name hexgrid so it’s result can be reused.

The Locations command has the following properties:

  • hexgrid refers to the assigned name for the Hexagons grid

  • "0204, 0101" are the co-ordinates of the two hexagons in the grid

  • a list of shapes

The list contains two shapes — a Circle and a Dot; these will be drawn in that order, each at the centre of the hexagon matching the co-ordinates that have been set.

Example 2. Locations & Sequence

^

ml1

This example shows locations constructed using the command:

hexgrid = Hexagons(
    side=0.5,
    x=0, y=0,
    rows=6, cols=4,
)
Locations(
    hexgrid,
    "all",
    [circle(common=a_circle, label="s{{sequence}}")]
)

The Hexagons grid is constructed as per the examples described in the Rectangular Hexagonal Grid section.

The grid is assigned the name hexgrid so it’s result can be reused.

The Locations command has the following properties:

  • hexgrid refers to the assigned name for the Hexagons grid

  • "all" is a short-cut which refers to all the co-ordinates of the hexagons in the grid

  • a list, with a shape

The list contains a single shape — a Circle whose label has been set to the reference keyword {{sequence}}.

Because of the enclosing brackets {{...}} the keyword will be replaced by the actual value of the sequence number in which the hexagon has been drawn.

Example 3. Locations & Labels

^

ml2

This example shows locations constructed using the command:

hexgrid = Hexagons(
    side=0.5,
    x=0, y=0,
    rows=6, cols=4,
)
Locations(
    hexgrid,
    "all",
    [circle(common=a_circle, label="l{{label}}")]
)

The Hexagons grid is constructed as per the examples described in the Rectangular Hexagonal Grid section.

The grid is assigned the name hexgrid so it’s result can be reused.

The Locations command has the following properties:

  • hexgrid refers to the assigned name for the Hexagons grid

  • "all" is a short-cut which refers to all the co-ordinates of the hexagons in the grid

  • a list, with a shape

The list contains a single shape — a Circle whose label has been set to the reference keyword {{label}}

Because of the enclosing brackets {{...}} the keyword will be replaced by the actual value of hexagon co-ordinates in which the circle has been drawn.

Example 4. Locations & Col/Row

^

ml3

This example shows locations constructed using the command:

hexgrid = Hexagons(
    side=0.5,
    x=0, y=0,
    rows=6, cols=4,
)
Locations(
    hexgrid,
    "all",
    [circle(
        common=a_circle,
        label="c{{col}}r{{row}}")]
)

The Hexagons grid is constructed as per the examples described in the Rectangular Hexagonal Grid section.

The grid is assigned the name hexgrid so it’s result can be reused.

The Locations command has the following properties:

  • hexgrid refers to the assigned name for the Hexagons grid

  • "all" is a short-cut which refers to all the co-ordinates of the hexagons in the grid

  • a list, with a shape

The list contains a single shape — a Circle whose label has been set to use the reference keywords {{col}} and {{row}}.

Because of the enclosing brackets {{...}} the keywords will be replaced by the actual value of the property of the hexagon in which the circle has been drawn.

Grid LinkLine

The LinkLine() command allows the creation of a line to join one or more hexagons within a hexagonal grid.

This command should work with any of the types of hexagonal grid layouts described above.

All of the examples below make use of the same underlying hexagonal grid:

hexgrid = Hexagons(
    side=0.5,
    x=0, y=0,
    rows=6, cols=4,
    dot=0.02,
    coord_elevation='top'
)

The grid is assigned the name hexgrid so its result can be reused.

Example 1. A Single LinkLine

^

ll0

This example shows a LinkLine constructed using the command:

LinkLine(
    grid=hexgrid,
    locations="0101,0403"
)

The LinkLine command has the following properties:

  • grid used is hexgrid (as defined for all these examples)

  • locations - set to "0101,0403"

The locations represent the coordinates of the start and end locations in the grid, between which the line is drawn.

By default, the Linkline uses the x and y values of the centre of the hexagon in which it starts or ends, and uses the default styling.

Example 2. A Double LinkLine

^

ll1

This example shows a LinkLine constructed using the command:

LinkLine(
    hexgrid,
    "0101,0403,0104"
)

The LinkLine command has the following properties:

  • the grid used is hexgrid (as defined for all these examples)

  • locations are set to "0101,0403,0104"

The string contains the coordinates of multiple start and end locations in the grid, between which the line is drawn.

The first lines is drawn between the first and second hexagon; the second between the second and third hexagon specified.

By default, the Linkline uses the x and y values of the centre of the hexagon in which it starts or ends, and uses the default styling.

Note that in this example, the grid= and locations= are omitted; the program can just use the values presented, provided they are in the correct order shown above.

Example 3. A Styled LinkLine

^

ll2

This example shows a LinkLine constructed using the command:

LinkLine(
    hexgrid,
    ["0101", "0403", "0104", "0406"],
    common=Common(
        stroke="tomato",
        stroke_width=2)
)
LinkLine(
    hexgrid,
    ["0104", "0406"],
    common=Common(
        stroke="cyan",
        stroke_width=2)
)

The LinkLine commands have the following properties:

  • the grid used is hexgrid (as defined for all these examples)

  • ["0101","0403","0104","0406"] - location coordinates

  • ["0104","0406"] - location coordinates

  • common - defines the styling for the line

The location coordinates contain multiple start and end locations in the grid, between which the lines are drawn.

In this example, the locations are defined as individual strings in a list.

By default, the lines use the x and y values of the centre of the hex in which they start or end.

Example 4. An Offset LinkLine

^

ll3

This example shows a LinkLine constructed using the command:

LinkLine(
    hexgrid,
    [("0101", 0.25, 0.25),
     ("0403", -0.25, -0.25),
     ("0104", 0.0, 0.25),
     ("0104", 0.25, -0.25)],
    common=Common(
        stroke="tomato",
        stroke_width=1,
        dotted=True)
)

The LinkLine command has the following properties:

  • the grid used is hexgrid (as defined for all these examples)

  • ("0101", 0.25, 0.25) - coordinates of a grid location and the offset values

  • common - this third property defines the styling for the line

The offset values — x and y — are relative to the centre of the hex in which the line starts or ends.

Positive values for the offset move the x and y down and to the right of the centre; negatives move the x and y up and to the left of the centre.

Note that its possible to define the start and end as different offsets within the same hexagon; as shown here in location 0104.

Other Hexagonal Grid Resources

There are already a number of software tools available for creating hexagonal grids of various kinds and for different purposes.

A few of these tools, some of which are game-specific, for example, the so-called 18XX series, are listed below:

The options and facilities provided by these tools have been the primary inspiration for how hexagonal grids work in protograf. If the functionality available here does not work for you, then possibly one of these other tools would be of better fit.

Hint

For everything — and I mean everything — related to how hexagonal grids are designed and calculated, the single most useful reference for a designer is https://www.redblobgames.com/grids/hexagons/

An 18XX Footnote

The 18XX game series hex maps are often criticised for their poor aesthetic. A fascinating article that deals with this topic - and is perhaps relevant even at the prototyping stage being supported by this program - can be found at https://medium.com/grandtrunkgames/mawgd4-18xx-tiles-and-18xx-maps-8a409bba4230