Skip to content

Commit

Permalink
[bgw-gui] Hexagon grid with OFFSET and AXIAL coordinate systems (#373)
Browse files Browse the repository at this point in the history
* Added hexagon view component.

* Added sample builder for hexagon view.

* Display hexagons according to given size.

* Refactored building hexagons. Added new hexagon builder.

* Build compound visuals for hexagon view.

* Added safe element indexing in observable lists.

* Refactored hexagon view constructor parameter to take radius instead of diameter.

* Added empty hexagon view animation to animation builder.

* Fixed mouse picking fo hexagon views with overlapping layout bounds.

* Reformatted ComponentNodeBuilder.kt

* 🎉 Added hexagon grid game component container 🎉

* Added new coordinate system enum.

* Added CoordinateSystem to HexagonGrid.

* Wrapped hexagon views in region for proper layouting.

* Added FlipAnimation support for Hexagons.

* Added documentation for hexagons.

* Applied spotless.

* Added KDocs for HexagonGrid.

* Updated CHANGELOG.md

* Added pages documentation with examples and images.

---------

Co-authored-by: Amin Bouzerda <amin.bouzerda@tu-dortmund.de>
  • Loading branch information
abouzerda and Amin Bouzerda authored May 17, 2023
1 parent 84af926 commit ec87e64
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 1 deletion.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@
# Changelog
All notable changes to this project will be documented in this file.

## [0.8] - 22.05.2023
## [0.8] - 22.05.2022

### Added
- `CameraPane` component.
- `HexagonView` component.
- `HexagonGrid` with two coordinate systems. axial and offset coordinates.

## [0.7.3] - 31.08.2022

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 48 additions & 0 deletions bgw-docs/src/main/jekyll/components/container/container.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ nav_order: 4
[CardStackKDoc]: ../../bgw-gui-kdoc/bgw-gui/tools.aqua.bgw.components.container/-card-stack/index.html
[LinearLayoutKDoc]: ../../bgw-gui-kdoc/bgw-gui/tools.aqua.bgw.components.container/-linear-layout/index.html
[SatchelKDoc]: ../../bgw-gui-kdoc/bgw-gui/tools.aqua.bgw.components.container/-satchel/index.html
[HexagonGridKDoc]: ../../bgw-gui-kdoc/bgw-gui/tools.aqua.bgw.components.container/-hexagon-grid/index.html
[HexagonKDoc]: ../../bgw-gui-kdoc/bgw-gui/tools.aqua.bgw.components.gamecomponentviews/-hexagon-view/index.html


<!-- GH-Pages Doc -->
[ComponentViewDoc]: ../../components/componentview/componentview.md
Expand Down Expand Up @@ -175,6 +178,51 @@ visualize a hand of cards:
A satchel hides its /components and reveals them, when they are removed. This container can be used to visualize an
entity, where the user should not know what might get drawn next, or what is in the container.

### [HexagonGrid][HexagonGridKDoc]

Represents a grid of hexagons in a coordinate system.
Each hexagon can be accessed and manipulated using column and row indices.

### Coordinate Systems
The HexagonGrid class supports two coordinate systems: offset and axial.

### Offset Coordinate System
In the offset coordinate system, the hexagons are positioned using a grid of rectangular offsets. Each hexagon occupies a rectangular cell, and the coordinate values represent the row and column indices of the hexagons in the grid.

![image](offset.png)

### Axial Coordinate System
In the axial coordinate system, the hexagons are positioned using axial coordinates. Each hexagon is defined by two axial coordinates: q (column) and r (row). The axial coordinates represent the column and row indices of the hexagons in the grid.

![image](axial.png)


### Example

```kotlin
val hexagonGrid: HexagonGrid<HexagonView> = HexagonGrid()

for (row in 0..4) {
for (col in 0..4) {
val hexagon = HexagonView(visual = ColorVisual.RED)
hexagonGrid[col, row] = hexagon
}
}
```

Here is an example on how to change the default coordinate system to axial.

```kotlin
val hexagonGrid: HexagonGrid<HexagonView> = HexagonGrid(coordinateSystem = CoordinateSystem.AXIAL)

for (row in -2..2) {
for (col in -2..2) {
val hexagon = HexagonView(visual = ColorVisual.BLUE)
hexagonGrid[col, row] = hexagon
}
}
```

## Container overview

[View it on GitHub](https://github.com/tudo-aqua/bgw/tree/main/bgw-examples/bgw-docs-examples/src/main/kotlin/examples/components/container/ContainerExample.kt){:
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Copyright 2023 The BoardGameWork Authors
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package tools.aqua.bgw.components.container

import tools.aqua.bgw.components.gamecomponentviews.HexagonView
import tools.aqua.bgw.visual.Visual

private typealias OffsetCoordinate = Pair<Int, Int>

private typealias AxialCoordinate = Pair<Int, Int>

/**
* A class representing a grid of hexagons.
*
* @param posX The x-coordinate of the hexagon grid's position on the screen. Default is 0.
* @param posY The y-coordinate of the hexagon grid's position on the screen. Default is 0.
* @param width The width of the hexagon grid. It grows dynamically by the amount hexagons in it.
* @param height The height of the hexagon grid. It grows dynamically by the amount hexagons in it.
* @param visual The visual representation of the hexagon grid. Default is an empty visual.
* @param coordinateSystem The coordinate system to use for the grid. Default is `CoordinateSystem.OFFSET`.
* @param T The type of the hexagon view. Must extend the `HexagonView` class.
*/
class HexagonGrid<T : HexagonView>(
posX: Number = 0,
posY: Number = 0,
width: Number = 0,
height: Number = 0,
visual: Visual = Visual.EMPTY,
coordinateSystem: CoordinateSystem = CoordinateSystem.OFFSET
) :
GameComponentContainer<T>(
posX = posX, posY = posY, width = width, height = height, visual = visual
) {

/**
* A mutable map that stores the hexagons in the grid.
*/
internal val map: MutableMap<OffsetCoordinate, T> = mutableMapOf()

/**
* Gets the hexagon at the specified column index and row index.
*
* @param columnIndex The column index of the hexagon.
* @param rowIndex The row index of the hexagon.
* @return The hexagon at the specified coordinates, or null if no hexagon is found.
*/
operator fun get(columnIndex: Int, rowIndex: Int): T? = map[columnIndex to rowIndex]

/**
* Sets the hexagon at the specified column index and row index.
*
* @param columnIndex The column index of the hexagon.
* @param rowIndex The row index of the hexagon.
* @param component The hexagon component to set.
*/
operator fun set(columnIndex: Int, rowIndex: Int, component: T) {
map[columnIndex to rowIndex]?.run { observableComponents.remove(this) }
map[columnIndex to rowIndex] = component
observableComponents.add(component)
}

init {
observableComponents.setInternalListenerAndInvoke(emptyList()) { _, _ ->
layout(coordinateSystem)
}
}

/**
* Internal function to layout the hexagons in the grid based on the specified coordinate system.
*
* @param coordinateSystem The coordinate system to use for the layout.
*/
private fun layout(coordinateSystem: CoordinateSystem) {
map.forEach { (x, y), hexagon ->
val (q, r) = when (coordinateSystem) {
CoordinateSystem.OFFSET -> x to y
CoordinateSystem.AXIAL -> x + (y - (y and 1)) / 2 to y
}
with(hexagon) {
posXProperty.setSilent(width * q + if (r % 2 == 0) 0.0 else width / 2)
posYProperty.setSilent(height * r - r * height / 4)
}
}
}

override fun T.onRemove() {}
override fun T.onAdd() {}

/**
* Enumeration class representing the coordinate system options for the hexagon grid.
*/
enum class CoordinateSystem {
OFFSET,
AXIAL
}
}

0 comments on commit ec87e64

Please sign in to comment.