From 266db439e4443d10bc1ec7eb7d9032f29daf6981 Mon Sep 17 00:00:00 2001 From: Luca Raddatz Date: Wed, 31 Jan 2024 13:38:44 +0100 Subject: [PATCH] [bgw-gui] Added HexOrientation to HexagonGrid and HexagonView (#407) * Added option for flat hexagon grid and hexagon views * Updated documentation to fix missing and wrong Hexagon information * Added missing changelog entries for older pre-release versions * Fixed hexagon grid to use q and r in docs --- CHANGELOG.md | 22 +++++++++++ .../jekyll/components/container/container.md | 19 +++++++-- .../gamecomponents/gamecomponents.md | 2 + .../tools/aqua/bgw/builder/HexagonBuilder.kt | 39 +++++++++++++------ .../bgw/components/container/HexagonGrid.kt | 30 +++++++++++--- .../gamecomponentviews/HexagonView.kt | 5 ++- .../tools/aqua/bgw/core/HexOrientation.kt | 24 ++++++++++++ 7 files changed, 119 insertions(+), 22 deletions(-) create mode 100644 bgw-gui/src/main/kotlin/tools/aqua/bgw/core/HexOrientation.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index a74d0aaee2..4f0e5bc5a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,28 @@ # Changelog All notable changes to this project will be documented in this file. +## 0.9.5-Pre - 30.01.2024 + +### Added +- Added `HexOrientation` to `HexagonGrid` and `HexagonView` to support different orientations of hexagons. + +## 0.9.4-Pre - 15.01.2024 + +### Fixed +- Fixed `TextInputUIComponent` not showing prompt text when text is empty. + +## 0.9.2-Pre - 15.12.2023 + +### Added +- Added missing `Cursor` CSS styles. + +### Changed +- Refactored `Visual` styling properties observer. +- Propagate compound visual styles to children. + +### Fixed +- Fixed CSS style properties not being set correctly. + ## [0.9] - 27.11.2023 ### Added diff --git a/bgw-docs/src/main/jekyll/components/container/container.md b/bgw-docs/src/main/jekyll/components/container/container.md index aac5afe7f8..f891c0d74d 100644 --- a/bgw-docs/src/main/jekyll/components/container/container.md +++ b/bgw-docs/src/main/jekyll/components/container/container.md @@ -215,14 +215,25 @@ Here is an example on how to change the default coordinate system to axial. ```kotlin val hexagonGrid: HexagonGrid = HexagonGrid(coordinateSystem = CoordinateSystem.AXIAL) -for (row in -2..2) { - for (col in -2..2) { - val hexagon = HexagonView(visual = ColorVisual.BLUE) - hexagonGrid[col, row] = hexagon +for (q in -2..2) { + for (r in -2..2) { + if (q + r >= -2 && q + r <= 2) { + val hexagon = HexagonView(visual = ColorVisual.BLUE) + hexagonGrid[q, r] = hexagon + } } } ``` +### Hexagon Grid Orientations +The HexagonGrid class supports two orientations: pointy top and flat top. + +### Pointy Top Orientation +**This is the default orientation for a HexGrid.** In the pointy top orientation (`HexOrientation.POINTY_TOP`), the hexagons are positioned with their tips pointing up and down. + +### Flat Top Orientation +In the flat top orientation (`HexOrientation.FLAT_TOP`), the hexagons are positioned with their tips pointing left and right. + ## 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){: diff --git a/bgw-docs/src/main/jekyll/components/gamecomponents/gamecomponents.md b/bgw-docs/src/main/jekyll/components/gamecomponents/gamecomponents.md index 7db0f1257f..a9ac356796 100644 --- a/bgw-docs/src/main/jekyll/components/gamecomponents/gamecomponents.md +++ b/bgw-docs/src/main/jekyll/components/gamecomponents/gamecomponents.md @@ -69,4 +69,6 @@ For a dice roll there exists a dedicated [DiceAnimation][DiceAnimationDoc]. The [HexagonView][HexagonDoc] component can also be utilized for hexagonal tokens, providing a versatile solution for displaying various hexagonal-shaped elements. +A Hexagon by default has its tips pointing up and down (`HexOrientation.POINTY_TOP`). It can however also have its tips pointing left and right (`HexOrientation.FLAT_TOP`). + Hexagons only have one visual. \ No newline at end of file diff --git a/bgw-gui/src/main/kotlin/tools/aqua/bgw/builder/HexagonBuilder.kt b/bgw-gui/src/main/kotlin/tools/aqua/bgw/builder/HexagonBuilder.kt index b8505706b5..7f95731bc6 100644 --- a/bgw-gui/src/main/kotlin/tools/aqua/bgw/builder/HexagonBuilder.kt +++ b/bgw-gui/src/main/kotlin/tools/aqua/bgw/builder/HexagonBuilder.kt @@ -33,6 +33,7 @@ import kotlin.math.sqrt import tools.aqua.bgw.builder.FXConverters.toFXColor import tools.aqua.bgw.builder.FXConverters.toFXImage import tools.aqua.bgw.components.gamecomponentviews.HexagonView +import tools.aqua.bgw.core.HexOrientation import tools.aqua.bgw.style.color import tools.aqua.bgw.style.pixel import tools.aqua.bgw.visual.* @@ -53,7 +54,7 @@ object HexagonBuilder { /** Builds [HexagonView]. */ internal fun buildHexagonView(hexagonView: HexagonView): Region { val root = Pane().apply { isPickOnBounds = false } - val points = generatePoints(hexagonView.size.toDouble()) + val points = generatePoints(hexagonView.size.toDouble(), orientation = hexagonView.orientation) hexagonView.visualProperty.setGUIListenerAndInvoke(hexagonView.visual) { _, nV -> root.children.clear() val component = @@ -107,14 +108,27 @@ object HexagonBuilder { nV.pixel.toDouble() } else 0.0 } - visual.borderRadiusProperty.addListenerAndInvoke(visual.borderRadius) { _, nV -> - clip = - if (nV != null && nV.pixel > 0) { - val size = hexagonView.size.toDouble() - nV.pixel.toDouble() - val offsetX = hexagonView.widthProperty.value / 2 - sqrt(3.0) / 2 * size - val offsetY = hexagonView.heightProperty.value / 2 - size - Polygon(*generatePoints(size, offsetX, offsetY)).apply { roundCorners() } - } else null + + if (hexagonView.orientation == HexOrientation.POINTY_TOP) { + visual.borderRadiusProperty.addListenerAndInvoke(visual.borderRadius) { _, nV -> + clip = + if (nV != null && nV.pixel > 0) { + val size = hexagonView.size.toDouble() - nV.pixel.toDouble() + val offsetX = hexagonView.widthProperty.value / 2 - sqrt(3.0) / 2 * size + val offsetY = hexagonView.heightProperty.value / 2 - size + Polygon(*generatePoints(size, offsetX, offsetY)).apply { roundCorners() } + } else null + } + } else { + visual.borderRadiusProperty.addListenerAndInvoke(visual.borderRadius) { _, nV -> + clip = + if (nV != null && nV.pixel > 0) { + val size = hexagonView.size.toDouble() - nV.pixel.toDouble() + val offsetX = hexagonView.widthProperty.value / 2 - size + val offsetY = hexagonView.heightProperty.value / 2 - sqrt(3.0) / 2 * size + Polygon(*generatePoints(size, offsetX, offsetY)).apply { roundCorners() } + } else null + } } }) .apply { isPickOnBounds = false } @@ -141,10 +155,13 @@ object HexagonBuilder { private fun generatePoints( size: Double, offsetX: Number = 0.0, - offsetY: Number = 0.0 + offsetY: Number = 0.0, + orientation: HexOrientation = HexOrientation.POINTY_TOP ): DoubleArray { val points = mutableListOf() - var angle = 90.0 + + var angle = if (orientation == HexOrientation.POINTY_TOP) 30.0 else 0.0 + repeat(HEXAGON_SIDES) { val x = size * cos(Math.toRadians(angle)) + size val y = size * sin(Math.toRadians(angle)) + size diff --git a/bgw-gui/src/main/kotlin/tools/aqua/bgw/components/container/HexagonGrid.kt b/bgw-gui/src/main/kotlin/tools/aqua/bgw/components/container/HexagonGrid.kt index 26659d161e..509f551901 100644 --- a/bgw-gui/src/main/kotlin/tools/aqua/bgw/components/container/HexagonGrid.kt +++ b/bgw-gui/src/main/kotlin/tools/aqua/bgw/components/container/HexagonGrid.kt @@ -19,6 +19,7 @@ package tools.aqua.bgw.components.container import kotlin.math.sqrt import tools.aqua.bgw.components.gamecomponentviews.HexagonView +import tools.aqua.bgw.core.HexOrientation import tools.aqua.bgw.visual.Visual private typealias OffsetCoordinate = Pair @@ -35,6 +36,8 @@ private typealias AxialCoordinate = Pair * @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 orientation The orientation of the hexagons in the grid. Default is + * `HexOrientation.POINTY_TOP`. */ class HexagonGrid( posX: Number = 0, @@ -42,7 +45,8 @@ class HexagonGrid( width: Number = 0, height: Number = 0, visual: Visual = Visual.EMPTY, - coordinateSystem: CoordinateSystem = CoordinateSystem.OFFSET + coordinateSystem: CoordinateSystem = CoordinateSystem.OFFSET, + var orientation: HexOrientation = HexOrientation.POINTY_TOP ) : GameComponentContainer( posX = posX, posY = posY, width = width, height = height, visual = visual) { @@ -74,6 +78,7 @@ class HexagonGrid( */ operator fun set(columnIndex: Int, rowIndex: Int, component: T) { map[columnIndex to rowIndex]?.run { observableComponents.remove(this) } + component.orientation = orientation map[columnIndex to rowIndex] = component observableComponents.add(component) } @@ -87,13 +92,26 @@ class HexagonGrid( 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 + CoordinateSystem.OFFSET -> { + if (orientation == HexOrientation.POINTY_TOP) x to y else y to x + } + CoordinateSystem.AXIAL -> { + if (orientation == HexOrientation.POINTY_TOP) x + (y - (y and 1)) / 2 to y + else y + (x - (x and 1)) / 2 to x + } } with(hexagon) { - val actualWidth = width / 2 * sqrt(3.0) - posXProperty.setSilent(actualWidth * q + if (r % 2 == 0) 0.0 else actualWidth / 2) - posYProperty.setSilent(height * r - r * height / 4) + if (orientation == HexOrientation.POINTY_TOP) { + hexagon.orientation = HexOrientation.POINTY_TOP + val actualWidth = width / 2 * sqrt(3.0) + posXProperty.setSilent(actualWidth * q + if (r % 2 == 0) 0.0 else actualWidth / 2) + posYProperty.setSilent(height * r - r * height / 4) + } else { + hexagon.orientation = HexOrientation.FLAT_TOP + val actualHeight = height / 2 * sqrt(3.0) + posYProperty.setSilent(actualHeight * q + if (r % 2 == 0) 0.0 else actualHeight / 2) + posXProperty.setSilent(width * r - r * width / 4) + } } } } diff --git a/bgw-gui/src/main/kotlin/tools/aqua/bgw/components/gamecomponentviews/HexagonView.kt b/bgw-gui/src/main/kotlin/tools/aqua/bgw/components/gamecomponentviews/HexagonView.kt index 0f4a8b5fc5..e8983dff22 100644 --- a/bgw-gui/src/main/kotlin/tools/aqua/bgw/components/gamecomponentviews/HexagonView.kt +++ b/bgw-gui/src/main/kotlin/tools/aqua/bgw/components/gamecomponentviews/HexagonView.kt @@ -18,6 +18,7 @@ package tools.aqua.bgw.components.gamecomponentviews import tools.aqua.bgw.core.DEFAULT_HEXAGON_SIZE +import tools.aqua.bgw.core.HexOrientation import tools.aqua.bgw.visual.Visual /** @@ -30,12 +31,14 @@ import tools.aqua.bgw.visual.Visual * @param size Represents the radius of the outer circle of the [HexagonView] all six points lie on. * Default: [DEFAULT_HEXAGON_SIZE]. * @param visual Visual for this [HexagonView]. + * @param orientation Orientation of the [HexagonView]. Default: [HexOrientation.POINTY_TOP]. */ open class HexagonView( posX: Number = 0, posY: Number = 0, val size: Number = DEFAULT_HEXAGON_SIZE, - visual: Visual + visual: Visual, + var orientation: HexOrientation = HexOrientation.POINTY_TOP ) : GameComponentView( posX = posX, diff --git a/bgw-gui/src/main/kotlin/tools/aqua/bgw/core/HexOrientation.kt b/bgw-gui/src/main/kotlin/tools/aqua/bgw/core/HexOrientation.kt new file mode 100644 index 0000000000..c2f8858729 --- /dev/null +++ b/bgw-gui/src/main/kotlin/tools/aqua/bgw/core/HexOrientation.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2024 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.core + +/** Enumeration class representing the orientation options for hexagonal grids and views. */ +enum class HexOrientation { + POINTY_TOP, + FLAT_TOP +}