chore: Simplified hex coord math - part 2

This commit is contained in:
Yair Morgenstern 2023-02-05 00:59:28 +02:00
parent e390a4f579
commit a9903ad0f5
3 changed files with 58 additions and 57 deletions

View File

@ -126,49 +126,49 @@ object HexMath {
// y is 2 o'clock - increases column by 1, x in 10 o'clock - decreases by 1
fun getColumn(hexCoord: Vector2): Int = (hexCoord.y - hexCoord.x).toInt()
fun hex2CubicCoords(hexCoord: Vector2): Vector3 {
return Vector3(hexCoord.y - hexCoord.x, hexCoord.x, -hexCoord.y)
}
fun cubic2HexCoords(cubicCoord: Vector3): Vector2 {
return Vector2(cubicCoord.y, -cubicCoord.z)
}
fun evenQ2CubicCoords(evenQCoord: Vector2): Vector3 {
val x = evenQCoord.x
val z = evenQCoord.y - (evenQCoord.x + (evenQCoord.x.toInt() and 1)) / 2
val y = -x - z
return Vector3(x, y, z)
}
fun evenQ2HexCoords(evenQCoord: Vector2): Vector2 {
return if (evenQCoord == Vector2.Zero)
Vector2.Zero
else
cubic2HexCoords(evenQ2CubicCoords(evenQCoord))
}
fun roundCubicCoords(cubicCoords: Vector3): Vector3 {
var rx = round(cubicCoords.x)
var ry = round(cubicCoords.y)
var rz = round(cubicCoords.z)
val deltaX = abs(rx - cubicCoords.x)
val deltaY = abs(ry - cubicCoords.y)
val deltaZ = abs(rz - cubicCoords.z)
if (deltaX > deltaY && deltaX > deltaZ)
rx = -ry - rz
else if (deltaY > deltaZ)
ry = -rx - rz
else
rz = -rx - ry
return Vector3(rx, ry, rz)
fun getTileCoordsFromColumnRow(column: Int, row: Int): Vector2 {
// we know that column = y-x in hex coords
// And we know that row = y+x in hex coords
// Therefore, row+column = 2y, row-column=2x
// However, these row numbers only apear on alternating columns.
// So column 0 will have rows 0,2,4, etc, and column 1 will have rows 1,3,5 etc.
// you'll need to see a hexmap to see it, and then it will be obvious
val adjustedRow = if (column%2 == 0) row*2f else row*2f + 1
return Vector2((adjustedRow-column)/2, (adjustedRow+column)/2)
}
/** Todo: find a mathematically equivalent way to round hex coords without cubic magic */
fun roundHexCoords(hexCoord: Vector2): Vector2 {
fun roundCubicCoords(cubicCoords: Vector3): Vector3 {
var rx = round(cubicCoords.x)
var ry = round(cubicCoords.y)
var rz = round(cubicCoords.z)
val deltaX = abs(rx - cubicCoords.x)
val deltaY = abs(ry - cubicCoords.y)
val deltaZ = abs(rz - cubicCoords.z)
if (deltaX > deltaY && deltaX > deltaZ)
rx = -ry - rz
else if (deltaY > deltaZ)
ry = -rx - rz
else
rz = -rx - ry
return Vector3(rx, ry, rz)
}
fun hex2CubicCoords(hexCoord: Vector2): Vector3 {
return Vector3(hexCoord.y - hexCoord.x, hexCoord.x, -hexCoord.y)
}
fun cubic2HexCoords(cubicCoord: Vector3): Vector2 {
return Vector2(cubicCoord.y, -cubicCoord.z)
}
return cubic2HexCoords(roundCubicCoords(hex2CubicCoords(hexCoord)))
}

View File

@ -115,10 +115,10 @@ class TileMap : IsPartOfGameInfoSerialization {
// Even widths will have coordinates ranging -x..(x-1), not -x..x, which is always an odd-sized range
// e.g. w=4 -> -2..1, w=5 -> -2..2, w=6 -> -3..2, w=7 -> -3..3
for (row in -wrapAdjustedWidth / 2 .. (wrapAdjustedWidth-1) / 2)
for (column in -height / 2 .. (height-1) / 2)
for (column in -wrapAdjustedWidth / 2 .. (wrapAdjustedWidth-1) / 2)
for (row in -height / 2 .. (height-1) / 2)
tileList.add(Tile().apply {
position = HexMath.evenQ2HexCoords(Vector2(row.toFloat(), column.toFloat()))
position = HexMath.getTileCoordsFromColumnRow(column, row)
baseTerrain = firstAvailableLandTerrain
})
@ -205,8 +205,8 @@ class TileMap : IsPartOfGameInfoSerialization {
}.filterNotNull()
/** @return all tiles within [rectangle], respecting world edges and wrap.
* If using even Q coordinates the rectangle will be "straight" ie parallel with rectangular map edges. */
fun getTilesInRectangle(rectangle: Rectangle, evenQ: Boolean = false): Sequence<Tile> =
* If using row/column coordinates the rectangle will be "straight" ie parallel with rectangular map edges. */
fun getTilesInRectangle(rectangle: Rectangle, rowsAndColumns: Boolean = false): Sequence<Tile> =
if (rectangle.width <= 0 || rectangle.height <= 0) {
val tile = getIfTileExistsOrNull(rectangle.x.toInt(), rectangle.y.toInt())
if (tile == null) sequenceOf()
@ -214,12 +214,13 @@ class TileMap : IsPartOfGameInfoSerialization {
}
else
sequence {
for (x in 0 until rectangle.width.toInt()) {
for (y in 0 until rectangle.height.toInt()) {
val currentX = rectangle.x + x
val currentY = rectangle.y + y
if (evenQ) {
val hexCoords = HexMath.evenQ2HexCoords(Vector2(currentX, currentY))
for (column in 0 until rectangle.width.toInt()) {
for (row in 0 until rectangle.height.toInt()) {
val currentX = rectangle.x + column
val currentY = rectangle.y + row
if (rowsAndColumns) {
val hexCoords = HexMath.getTileCoordsFromColumnRow(column, row)
yield(getIfTileExistsOrNull(hexCoords.x.toInt(), hexCoords.y.toInt()))
}
else

View File

@ -174,12 +174,12 @@ class MapRegions (val ruleset: Ruleset){
splitOffRegion.tileMap.getTilesInRectangle(Rectangle(
splitOffRegion.rect.x + splitPoint - 1, splitOffRegion.rect.y,
1f, splitOffRegion.rect.height),
evenQ = true)
rowsAndColumns = true)
else
splitOffRegion.tileMap.getTilesInRectangle(Rectangle(
splitOffRegion.rect.x, splitOffRegion.rect.y + splitPoint - 1,
splitOffRegion.rect.width, 1f),
evenQ = true)
rowsAndColumns = true)
cumulativeFertility += if (splitOffRegion.continentID == -1)
nextRect.sumOf { it.getTileFertility(false) }
@ -408,7 +408,7 @@ class MapRegions (val ruleset: Ruleset){
val fallbackTiles = HashSet<Vector2>()
// First check center
val centerTiles = region.tileMap.getTilesInRectangle(centerRect, evenQ = true)
val centerTiles = region.tileMap.getTilesInRectangle(centerRect, rowsAndColumns = true)
for (tile in centerTiles) {
if (tileData[tile.position]!!.isTwoFromCoast)
continue // Don't even consider tiles two from coast
@ -436,7 +436,7 @@ class MapRegions (val ruleset: Ruleset){
}
// Now check middle donut
val middleDonut = region.tileMap.getTilesInRectangle(middleRect, evenQ = true).filterNot { it in centerTiles }
val middleDonut = region.tileMap.getTilesInRectangle(middleRect, rowsAndColumns = true).filterNot { it in centerTiles }
riverTiles.clear()
wetTiles.clear()
dryTiles.clear()
@ -467,7 +467,7 @@ class MapRegions (val ruleset: Ruleset){
}
// Now check the outer tiles. For these we don't care about rivers, coasts etc
val outerDonut = region.tileMap.getTilesInRectangle(region.rect, evenQ = true).filterNot { it in centerTiles || it in middleDonut}
val outerDonut = region.tileMap.getTilesInRectangle(region.rect, rowsAndColumns = true).filterNot { it in centerTiles || it in middleDonut}
dryTiles.clear()
for (tile in outerDonut) {
if (region.continentID != -1 && region.continentID != tile.getContinent())
@ -1693,7 +1693,7 @@ class Region (val tileMap: TileMap, val rect: Rectangle, val continentID: Int =
val columnHasTile = HashSet<Int>()
tiles.clear()
for (tile in tileMap.getTilesInRectangle(rect, evenQ = true).filter {
for (tile in tileMap.getTilesInRectangle(rect, rowsAndColumns = true).filter {
continentID == -1 || it.getContinent() == continentID } ) {
val fertility = tile.getTileFertility(continentID != -1)
tiles.add(tile)