Repository navigation
Grid definitions
Though it's intuitive to define rows and columns as contiguous cells, that would mean that, depending on the hexagon's orientation, one type would end up a little wonky:
This would make it almost impossible to create certain shapes, including some basic ones like:
To ensure that it is possible to make any particular setup with sensible options, this library defines rows and columns as collinear cells. That is, each row and column comprises only the cells that are centered directly on its line, which gives rise to an unusual grid coordinate system.
Because of this design, the valid coordinate pairs depend on whether the even or the odd lines are inset. When it's the evens, the grid above changes to:
This might seem like a huge pain to keep track of, but one of the library's main features is the option to display the row and column in each cell during design and debugging, so it's trivial to get everything in the right spot when using it as a layout:
If you're using it as some sort of game grid, then you're already doing math anyway, and one more index offset or parity check here and there isn't a big deal.
The row count, the column count, and which lines to inset are three of the four
configuration options that define a Grid, which is the specialized collection
for these objects.
interface Grid {
val rowCount: Int
val columnCount: Int
val insetEvenLines: Boolean
val enableEdgeLines: Boolean
…
}The last property, enableEdgeLines, causes an extra line to be added on all
four sides of a grid in order to fill in the "holes" at the edges. That is, it
causes the component to be completely covered with cells (not counting
HexGridView's padding). This might seem like a Layout setting instead, but it
affects the cell count, so it needs to be handled with these other Grid options.
The following image shows the shield arrangement from above with edge lines enabled. The left image shows how it would look in normal use, with the extra edge cells highlighted here for illustration. The right image shows the unclipped draw with indices enabled, so you can see what's actually happening.
This feature is meant to be used only with grids that fully fill the containing component, since grids that don't fill can always have more lines added in the regular fashion. However, nothing prohibits or prevents this option from being employed in any other setup.
Unfortunately, relatively thick stroke widths can cause a visual problem with edge lines, too, due to the library calculating the grid's layout so that the main rows and columns fit exactly to bounds, even accounting for the different line thickness at the two main axis vertices – the pointy ends – compared to the others.
Those are the same grid, but the one on the right has edge lines enabled, and you can see how the out-of-bounds cells are inset by a noticeable measure. This could be remedied by adding an option to halve the stroke width consideration when edge lines are enabled.
The library defines the Grid.Address class to keep a pair of row and column
indices together.
data class Address(val row: Int, val column: Int)Grid itself is indexed by Grid.Address to access its Grid.State objects.
data class State(val isVisible: Boolean, val isSelected: Boolean)If you need to read the Grid at runtime, it acts somewhat like
a Map<Grid.Address, Grid.State>: it has a forEach() function for iteration
and a get indexed accessor.
var firstSelected: Grid.Address? = null
grid.forEach { address, state ->
if (state.isSelected) {
firstSelected = address
return@forEach
}
}
…
val currentState = grid[firstSelected!!]