Created
May 4, 2021 15:52
-
-
Save BomBardyGamer/73a600cc484cb1b7d991c09967daa027 to your computer and use it in GitHub Desktop.
Algorithm to get a chunk position in a spiral in O(1)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Calculates a chunk position from a given [id] in a spiral pattern. | |
* | |
* This algorithm was previously part of Krypton (https://github.com/KryptonMC/Krypton), however it was removed after | |
* it was no longer used. | |
* | |
* **Algorithm:** | |
* | |
* Given n, an index in the squared spiral | |
* p, the sum of a point in the inner square | |
* and a, the position on the current square | |
* | |
* n = p + a | |
* | |
* Credit for this algorithm goes to | |
* [davidonet](https://stackoverflow.com/users/1068670/davidonet) (for the original algorithm), | |
* and [Esophose](https://github.com/Esophose) (for the Kotlin conversion and modifications) | |
* | |
* See [here](https://stackoverflow.com/questions/398299/looping-in-a-spiral) for original | |
* | |
* @param id the id in the spiral | |
* @param xOffset an optional X offset | |
* @param zOffset an optional Z offset | |
* @return a [ChunkPosition] containing the calculated position in the spiral. | |
*/ | |
fun chunkInSpiral(id: Int, xOffset: Int = 0, zOffset: Int = 0): ChunkPosition { | |
// if the id is 0 then we know we're in the centre | |
if (id == 0) return ChunkPosition(0 + xOffset, 0 + zOffset) | |
val index = id - 1 | |
// compute radius (inverse arithmetic sum of 8 + 16 + 24 + ...) | |
val radius = floor((sqrt(index + 1.0) - 1) / 2) + 1 | |
// compute total point on radius -1 (arithmetic sum of 8 + 16 + 24 + ...) | |
val p = (8 * radius * (radius - 1)) / 2 | |
// points by face | |
val en = radius * 2 | |
// compute de position and shift it so the first is (-r, -r) but (-r + 1, -r) | |
// so the square can connect | |
val a = (1 + index - p) % (radius * 8) | |
return when (floor(a / (radius * 2)).toInt()) { | |
// find the face (0 = top, 1 = right, 2 = bottom, 3 = left) | |
0 -> ChunkPosition((a - radius).toInt() + xOffset, -radius.toInt() + zOffset) | |
1 -> ChunkPosition(radius.toInt() + xOffset, ((a % en) - radius).toInt() + zOffset) | |
2 -> ChunkPosition((radius - (a % en)).toInt() + xOffset, radius.toInt() + zOffset) | |
3 -> ChunkPosition(-radius.toInt() + xOffset, (radius - (a % en)).toInt() + zOffset) | |
else -> ChunkPosition.ZERO | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment