diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/commands/BlockLogReplay.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/commands/BlockLogReplay.kt index 7ba69ec..76bb6fa 100644 --- a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/commands/BlockLogReplay.kt +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/commands/BlockLogReplay.kt @@ -1,8 +1,10 @@ package cloud.kubelet.foundation.gjallarhorn.commands +import cloud.kubelet.foundation.gjallarhorn.render.BlockDiversityRenderer +import cloud.kubelet.foundation.gjallarhorn.render.BlockHeightMapRenderer +import cloud.kubelet.foundation.gjallarhorn.render.BlockImageRenderer +import cloud.kubelet.foundation.gjallarhorn.state.* import cloud.kubelet.foundation.gjallarhorn.util.compose -import cloud.kubelet.foundation.gjallarhorn.render.* -import cloud.kubelet.foundation.gjallarhorn.util.RandomColorKey import cloud.kubelet.foundation.gjallarhorn.util.savePngFile import cloud.kubelet.foundation.heimdall.view.BlockChangeView import com.github.ajalt.clikt.core.CliktCommand @@ -17,7 +19,6 @@ import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.lessEq import org.jetbrains.exposed.sql.transactions.transaction import org.slf4j.LoggerFactory -import java.awt.image.BufferedImage import java.time.Duration import java.time.Instant import java.util.concurrent.ConcurrentHashMap @@ -60,7 +61,7 @@ class BlockLogReplay : CliktCommand("Replay Block Logs", name = "replay-block-lo } val trackerPool = ScheduledThreadPoolExecutor(8) - val trackers = ConcurrentHashMap() + val trackers = ConcurrentHashMap() for (time in intervals) { trackerPool.submit { val index = intervals.indexOf(time) + 1 @@ -77,19 +78,20 @@ class BlockLogReplay : CliktCommand("Replay Block Logs", name = "replay-block-lo } logger.info("State Tracking Completed") val allBlockOffsets = trackers.map { it.value.calculateZeroBlockOffset() } - val globalBlockOffset = BlockPosition.maxOf(allBlockOffsets.asSequence()) + val globalBlockOffset = BlockCoordinate.maxOf(allBlockOffsets.asSequence()) val allBlockMaxes = trackers.map { it.value.calculateMaxBlock() } - val globalBlockMax = BlockPosition.maxOf(allBlockMaxes.asSequence()) + val globalBlockMax = BlockCoordinate.maxOf(allBlockMaxes.asSequence()) val globalBlockExpanse = BlockExpanse.offsetAndMax(globalBlockOffset, globalBlockMax) logger.info("Calculations Completed") - val renderState = render.createState() + val renderer = render.create(globalBlockExpanse) val renderPool = ScheduledThreadPoolExecutor(16) + val imagePadCount = trackers.size.toString().length for ((i, tracker) in trackers.entries) { renderPool.submit { - val count = trackers.size.toString().length - saveRenderImage(renderState, tracker, globalBlockExpanse, "-${i.toString().padStart(count, '0')}") + val suffix = "-${i.toString().padStart(imagePadCount, '0')}" + saveRenderImage(renderer, tracker, globalBlockExpanse, suffix) logger.info("Rendered Timelapse $i") } } @@ -102,25 +104,24 @@ class BlockLogReplay : CliktCommand("Replay Block Logs", name = "replay-block-lo val time = if (exactTimeAsString != null) Instant.parse(exactTimeAsString) else null val tracker = buildTrackerState(time, "Single-Time") val expanse = BlockExpanse.offsetAndMax(tracker.calculateZeroBlockOffset(), tracker.calculateMaxBlock()) - saveRenderImage(render.createState(), tracker, expanse) + saveRenderImage(render.create(expanse), tracker, expanse) } } - fun saveRenderImage(renderState: Any, tracker: BlockStateTracker, expanse: BlockExpanse, suffix: String = "") { - val state = BlockStateImage() - tracker.populateStateImage(state, expanse.offset) - val image = render.renderBufferedImage(renderState, state, expanse) + fun saveRenderImage(renderer: BlockImageRenderer, tracker: BlockLogTracker, expanse: BlockExpanse, suffix: String = "") { + val map = tracker.buildBlockMap(expanse.offset) + val image = renderer.render(map) image.savePngFile("${render.id}${suffix}.png") } - fun buildTrackerState(time: Instant?, job: String): BlockStateTracker { + fun buildTrackerState(time: Instant?, job: String): BlockLogTracker { val filter = compose( combine = { a, b -> a and b }, { time != null } to { BlockChangeView.time lessEq time!! } ) val tracker = - BlockStateTracker(if (considerAirBlocks) BlockTrackMode.AirOnDelete else BlockTrackMode.RemoveOnDelete) + BlockLogTracker(if (considerAirBlocks) BlockTrackMode.AirOnDelete else BlockTrackMode.RemoveOnDelete) val blockChangeCounter = AtomicLong() transaction(db) { @@ -131,7 +132,7 @@ class BlockLogReplay : CliktCommand("Replay Block Logs", name = "replay-block-lo val z = row[BlockChangeView.z] val block = row[BlockChangeView.block] - val location = BlockPosition(x.toLong(), y.toLong(), z.toLong()) + val location = BlockCoordinate(x.toLong(), y.toLong(), z.toLong()) if (changeIsBreak) { tracker.delete(location) } else { @@ -152,7 +153,7 @@ class BlockLogReplay : CliktCommand("Replay Block Logs", name = "replay-block-lo return tracker } - fun maybeTrimState(tracker: BlockStateTracker) { + fun maybeTrimState(tracker: BlockLogTracker) { if (fromCoordinate == null || toCoordinate == null) { return } @@ -160,8 +161,8 @@ class BlockLogReplay : CliktCommand("Replay Block Logs", name = "replay-block-lo val from = fromCoordinate!!.split(",").map { it.toLong() } val to = toCoordinate!!.split(",").map { it.toLong() } - val fromBlock = BlockPosition(from[0], 0, from[1]) - val toBlock = BlockPosition(to[0], 0, to[1]) + val fromBlock = BlockCoordinate(from[0], 0, from[1]) + val toBlock = BlockCoordinate(to[0], 0, to[1]) tracker.trimOutsideXAndZRange(fromBlock, toBlock) } @@ -169,17 +170,12 @@ class BlockLogReplay : CliktCommand("Replay Block Logs", name = "replay-block-lo @Suppress("unused") enum class RenderType( val id: String, - val createState: () -> Any, - val renderBufferedImage: (Any, BlockStateImage, BlockExpanse) -> BufferedImage + val create: (BlockExpanse) -> BlockImageRenderer ) { - TopDown("top-down", - { TopDownState(RandomColorKey()) }, - { state, image, expanse -> image.buildTopDownImage(expanse, (state as TopDownState).randomColorKey) }), - HeightMap("height-map", { }, { _, image, expanse -> image.buildHeightMapImage(expanse) }) + BlockDiversity("block-diversity", { expanse -> BlockDiversityRenderer(expanse) }), + HeightMap("height-map", { expanse -> BlockHeightMapRenderer(expanse) }) } - class TopDownState(val randomColorKey: RandomColorKey) - @Suppress("unused") enum class TimelapseMode(val id: String, val interval: Duration) { ByHour("hours", Duration.ofHours(1)), diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockDiversityRenderer.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockDiversityRenderer.kt new file mode 100644 index 0000000..895a42f --- /dev/null +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockDiversityRenderer.kt @@ -0,0 +1,27 @@ +package cloud.kubelet.foundation.gjallarhorn.render + +import cloud.kubelet.foundation.gjallarhorn.state.BlockExpanse +import cloud.kubelet.foundation.gjallarhorn.state.BlockMap +import cloud.kubelet.foundation.gjallarhorn.util.RandomColorKey +import java.awt.Color +import java.awt.image.BufferedImage + +class BlockDiversityRenderer(val expanse: BlockExpanse, quadPixelSize: Int = defaultQuadPixelSize) : BlockGridRenderer(quadPixelSize) { + private val randomColorKey = RandomColorKey() + + override fun render(map: BlockMap): BufferedImage = buildPixelQuadImage(expanse) { x, z -> + val maybeYBlocks = map.blocks[x]?.get(z) + if (maybeYBlocks == null) { + setPixelQuad(x, z, Color.white.rgb) + return@buildPixelQuadImage + } + val maxBlockState = maybeYBlocks.maxByOrNull { it.key }?.value + if (maxBlockState == null) { + setPixelQuad(x, z, Color.white.rgb) + return@buildPixelQuadImage + } + + val color = randomColorKey.map(maxBlockState.type) + setPixelQuad(x, z, color.rgb) + } +} diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockExpanse.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockExpanse.kt deleted file mode 100644 index b4676fc..0000000 --- a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockExpanse.kt +++ /dev/null @@ -1,13 +0,0 @@ -package cloud.kubelet.foundation.gjallarhorn.render - -class BlockExpanse( - val offset: BlockPosition, - val size: BlockPosition -) { - companion object { - fun offsetAndMax(offset: BlockPosition, max: BlockPosition) = BlockExpanse( - offset, - offset.applyAsOffset(max) - ) - } -} diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockGridRenderer.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockGridRenderer.kt new file mode 100644 index 0000000..d6cf3ec --- /dev/null +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockGridRenderer.kt @@ -0,0 +1,42 @@ +package cloud.kubelet.foundation.gjallarhorn.render + +import cloud.kubelet.foundation.gjallarhorn.state.BlockExpanse +import java.awt.Color +import java.awt.Rectangle +import java.awt.image.BufferedImage + +abstract class BlockGridRenderer(val quadPixelSize: Int = defaultQuadPixelSize) : BlockImageRenderer { + protected fun BufferedImage.setPixelQuad(x: Long, z: Long, rgb: Int) { + drawSquare(x * quadPixelSize, z * quadPixelSize, quadPixelSize.toLong(), rgb) + } + + protected fun BufferedImage.drawSquare(x: Long, y: Long, side: Long, rgb: Int) { + val graphics = createGraphics() + graphics.color = Color(rgb) + graphics.fill(Rectangle(x.toInt(), y.toInt(), side.toInt(), side.toInt())) + graphics.dispose() + } + + protected fun buildPixelQuadImage( + expanse: BlockExpanse, + callback: BufferedImage.(Long, Long) -> Unit + ): BufferedImage { + val widthInBlocks = expanse.size.x + val heightInBlocks = expanse.size.z + val widthInPixels = widthInBlocks.toInt() * quadPixelSize + val heightInPixels = heightInBlocks.toInt() * quadPixelSize + val bufferedImage = + BufferedImage(widthInPixels, heightInPixels, BufferedImage.TYPE_4BYTE_ABGR) + + for (x in 0 until widthInBlocks) { + for (z in 0 until heightInBlocks) { + callback(bufferedImage, x, z) + } + } + return bufferedImage + } + + companion object { + const val defaultQuadPixelSize = 4 + } +} diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockHeatMapRenderer.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockHeatMapRenderer.kt new file mode 100644 index 0000000..5dd2f43 --- /dev/null +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockHeatMapRenderer.kt @@ -0,0 +1,22 @@ +package cloud.kubelet.foundation.gjallarhorn.render + +import cloud.kubelet.foundation.gjallarhorn.state.BlockExpanse +import cloud.kubelet.foundation.gjallarhorn.util.ColorGradient +import cloud.kubelet.foundation.gjallarhorn.util.FloatClamp +import java.awt.Color +import java.awt.image.BufferedImage + +abstract class BlockHeatMapRenderer(quadPixelSize: Int = defaultQuadPixelSize) : BlockGridRenderer(quadPixelSize) { + protected fun buildHeatMapImage(expanse: BlockExpanse, clamp: FloatClamp, calculate: (Long, Long) -> Long?): BufferedImage = + buildPixelQuadImage(expanse) { x, z -> + val value = calculate(x, z) + val color = if (value != null) { + val floatValue = clamp.convert(value) + ColorGradient.HeatMap.getColorAtValue(floatValue) + } else { + Color.white + } + + setPixelQuad(x, z, color.rgb) + } +} diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockHeightMapRenderer.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockHeightMapRenderer.kt new file mode 100644 index 0000000..71f3cca --- /dev/null +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockHeightMapRenderer.kt @@ -0,0 +1,17 @@ +package cloud.kubelet.foundation.gjallarhorn.render + +import cloud.kubelet.foundation.gjallarhorn.state.BlockExpanse +import cloud.kubelet.foundation.gjallarhorn.state.BlockMap +import cloud.kubelet.foundation.gjallarhorn.util.FloatClamp +import java.awt.image.BufferedImage + +class BlockHeightMapRenderer(val expanse: BlockExpanse, quadPixelSize: Int = defaultQuadPixelSize) : + BlockHeatMapRenderer(quadPixelSize) { + override fun render(map: BlockMap): BufferedImage { + val yMin = map.blocks.minOf { xSection -> xSection.value.minOf { zSection -> zSection.value.minOf { it.key } } } + val yMax = map.blocks.maxOf { xSection -> xSection.value.maxOf { zSection -> zSection.value.maxOf { it.key } } } + val clamp = FloatClamp(yMin, yMax) + + return buildHeatMapImage(expanse, clamp) { x, z -> map.blocks[x]?.get(z)?.maxOf { it.key } } + } +} diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockImageRenderer.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockImageRenderer.kt new file mode 100644 index 0000000..beabdc2 --- /dev/null +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockImageRenderer.kt @@ -0,0 +1,5 @@ +package cloud.kubelet.foundation.gjallarhorn.render + +import java.awt.image.BufferedImage + +interface BlockImageRenderer : BlockMapRenderer diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockMapRenderer.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockMapRenderer.kt new file mode 100644 index 0000000..0600549 --- /dev/null +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockMapRenderer.kt @@ -0,0 +1,7 @@ +package cloud.kubelet.foundation.gjallarhorn.render + +import cloud.kubelet.foundation.gjallarhorn.state.BlockMap + +interface BlockMapRenderer { + fun render(map: BlockMap): T +} diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockPosition.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockPosition.kt deleted file mode 100644 index 0ef225a..0000000 --- a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockPosition.kt +++ /dev/null @@ -1,37 +0,0 @@ -package cloud.kubelet.foundation.gjallarhorn.render - -import java.util.* - -data class BlockPosition( - val x: Long, - val y: Long, - val z: Long -) { - override fun equals(other: Any?): Boolean { - if (other !is BlockPosition) { - return false - } - - return other.x == x && other.y == y && other.z == z - } - - override fun hashCode(): Int = Objects.hash(x, y, z) - - fun applyAsOffset(position: BlockPosition) = position.copy( - x = position.x + x, - y = position.y + y, - z = position.z + z - ) - - companion object { - val zero = BlockPosition(0, 0, 0) - - fun maxOf(positions: Sequence): BlockPosition { - val x = positions.maxOf { it.x } - val y = positions.maxOf { it.y } - val z = positions.maxOf { it.z } - - return BlockPosition(x, y, z) - } - } -} diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockStateImage.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockStateImage.kt deleted file mode 100644 index dc94dcd..0000000 --- a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockStateImage.kt +++ /dev/null @@ -1,89 +0,0 @@ -package cloud.kubelet.foundation.gjallarhorn.render - -import cloud.kubelet.foundation.gjallarhorn.util.ColorGradient -import cloud.kubelet.foundation.gjallarhorn.util.FloatClamp -import cloud.kubelet.foundation.gjallarhorn.util.RandomColorKey -import java.awt.Color -import java.awt.Rectangle -import java.awt.image.BufferedImage -import java.util.* - -class BlockStateImage { - private val blocks = TreeMap>>() - - fun put(position: BlockPosition, state: BlockState) { - blocks.getOrPut(position.x) { - TreeMap() - }.getOrPut(position.z) { - TreeMap() - }[position.y] = state - } - - fun buildTopDownImage(expanse: BlockExpanse, randomColorKey: RandomColorKey): BufferedImage { - return buildPixelQuadImage(expanse) { x, z -> - val maybeYBlocks = blocks[x]?.get(z) - if (maybeYBlocks == null) { - setPixelQuad(x, z, Color.white.rgb) - return@buildPixelQuadImage - } - val maxBlockState = maybeYBlocks.maxByOrNull { it.key }?.value - if (maxBlockState == null) { - setPixelQuad(x, z, Color.white.rgb) - return@buildPixelQuadImage - } - - val color = randomColorKey.map(maxBlockState.type) - setPixelQuad(x, z, color.rgb) - } - } - - fun buildHeightMapImage(expanse: BlockExpanse): BufferedImage { - val yMin = blocks.minOf { xSection -> xSection.value.minOf { zSection -> zSection.value.minOf { it.key } } } - val yMax = blocks.maxOf { xSection -> xSection.value.maxOf { zSection -> zSection.value.maxOf { it.key } } } - val clamp = FloatClamp(yMin, yMax) - - return buildHeatMapImage(expanse, clamp) { x, z -> blocks[x]?.get(z)?.maxOf { it.key } } - } - - fun buildHeatMapImage(expanse: BlockExpanse, clamp: FloatClamp, calculate: (Long, Long) -> Long?): BufferedImage = - buildPixelQuadImage(expanse) { x, z -> - val value = calculate(x, z) - val color = if (value != null) { - val floatValue = clamp.convert(value) - ColorGradient.HeatMap.getColorAtValue(floatValue) - } else { - Color.white - } - - setPixelQuad(x, z, color.rgb) - } - - private fun BufferedImage.setPixelQuad(x: Long, z: Long, rgb: Int) { - drawSquare(x * quadImageSize, z * quadImageSize, quadImageSize.toLong(), rgb) - } - - private fun BufferedImage.drawSquare(x: Long, y: Long, side: Long, rgb: Int) { - val graphics = createGraphics() - graphics.color = Color(rgb) - graphics.fill(Rectangle(x.toInt(), y.toInt(), side.toInt(), side.toInt())) - graphics.dispose() - } - - private fun buildPixelQuadImage(expanse: BlockExpanse, callback: BufferedImage.(Long, Long) -> Unit): BufferedImage { - val width = expanse.size.x - val height = expanse.size.z - val bufferedImage = - BufferedImage(width.toInt() * quadImageSize, height.toInt() * quadImageSize, BufferedImage.TYPE_4BYTE_ABGR) - - for (x in 0 until width) { - for (z in 0 until height) { - callback(bufferedImage, x, z) - } - } - return bufferedImage - } - - companion object { - const val quadImageSize = 4 - } -} diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockCoordinate.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockCoordinate.kt new file mode 100644 index 0000000..3ec4331 --- /dev/null +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockCoordinate.kt @@ -0,0 +1,37 @@ +package cloud.kubelet.foundation.gjallarhorn.state + +import java.util.* + +data class BlockCoordinate( + val x: Long, + val y: Long, + val z: Long +) { + override fun equals(other: Any?): Boolean { + if (other !is BlockCoordinate) { + return false + } + + return other.x == x && other.y == y && other.z == z + } + + override fun hashCode(): Int = Objects.hash(x, y, z) + + fun applyAsOffset(coordinate: BlockCoordinate) = coordinate.copy( + x = coordinate.x + x, + y = coordinate.y + y, + z = coordinate.z + z + ) + + companion object { + val zero = BlockCoordinate(0, 0, 0) + + fun maxOf(coordinates: Sequence): BlockCoordinate { + val x = coordinates.maxOf { it.x } + val y = coordinates.maxOf { it.y } + val z = coordinates.maxOf { it.z } + + return BlockCoordinate(x, y, z) + } + } +} diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockExpanse.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockExpanse.kt new file mode 100644 index 0000000..b2fa0a4 --- /dev/null +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockExpanse.kt @@ -0,0 +1,13 @@ +package cloud.kubelet.foundation.gjallarhorn.state + +class BlockExpanse( + val offset: BlockCoordinate, + val size: BlockCoordinate +) { + companion object { + fun offsetAndMax(offset: BlockCoordinate, max: BlockCoordinate) = BlockExpanse( + offset, + offset.applyAsOffset(max) + ) + } +} diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockStateTracker.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockLogTracker.kt similarity index 58% rename from tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockStateTracker.kt rename to tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockLogTracker.kt index 127b6a0..cc4ec78 100644 --- a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockStateTracker.kt +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockLogTracker.kt @@ -1,15 +1,15 @@ -package cloud.kubelet.foundation.gjallarhorn.render +package cloud.kubelet.foundation.gjallarhorn.state import kotlin.math.absoluteValue -class BlockStateTracker(private val mode: BlockTrackMode = BlockTrackMode.RemoveOnDelete) { - val blocks = HashMap() +class BlockLogTracker(private val mode: BlockTrackMode = BlockTrackMode.RemoveOnDelete) { + val blocks = HashMap() - fun place(position: BlockPosition, state: BlockState) { + fun place(position: BlockCoordinate, state: BlockState) { blocks[position] = state } - fun delete(position: BlockPosition) { + fun delete(position: BlockCoordinate) { if (mode == BlockTrackMode.AirOnDelete) { blocks[position] = BlockState("minecraft:air") } else { @@ -17,7 +17,7 @@ class BlockStateTracker(private val mode: BlockTrackMode = BlockTrackMode.Remove } } - fun trimOutsideXAndZRange(min: BlockPosition, max: BlockPosition) { + fun trimOutsideXAndZRange(min: BlockCoordinate, max: BlockCoordinate) { val blockPositionsToRemove = blocks.keys.filter { it.x < min.x || it.z < min.z || @@ -28,7 +28,7 @@ class BlockStateTracker(private val mode: BlockTrackMode = BlockTrackMode.Remove blockPositionsToRemove.forEach { blocks.remove(it) } } - fun calculateZeroBlockOffset(): BlockPosition { + fun calculateZeroBlockOffset(): BlockCoordinate { val x = blocks.keys.minOf { it.x } val y = blocks.keys.minOf { it.y } val z = blocks.keys.minOf { it.z } @@ -37,22 +37,24 @@ class BlockStateTracker(private val mode: BlockTrackMode = BlockTrackMode.Remove val yOffset = if (y < 0) y.absoluteValue else 0 val zOffset = if (z < 0) z.absoluteValue else 0 - return BlockPosition(xOffset, yOffset, zOffset) + return BlockCoordinate(xOffset, yOffset, zOffset) } - fun calculateMaxBlock(): BlockPosition { + fun calculateMaxBlock(): BlockCoordinate { val x = blocks.keys.maxOf { it.x } val y = blocks.keys.maxOf { it.y } val z = blocks.keys.maxOf { it.z } - return BlockPosition(x, y, z) + return BlockCoordinate(x, y, z) } fun isEmpty() = blocks.isEmpty() - fun populateStateImage(image: BlockStateImage, offset: BlockPosition = BlockPosition.zero) { + fun buildBlockMap(offset: BlockCoordinate = BlockCoordinate.zero): BlockMap { + val map = BlockMap() blocks.forEach { (position, state) -> val realPosition = offset.applyAsOffset(position) - image.put(realPosition, state) + map.put(realPosition, state) } + return map } } diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockMap.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockMap.kt new file mode 100644 index 0000000..5ae93af --- /dev/null +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockMap.kt @@ -0,0 +1,15 @@ +package cloud.kubelet.foundation.gjallarhorn.state + +import java.util.* + +class BlockMap { + val blocks = TreeMap>>() + + fun put(position: BlockCoordinate, state: BlockState) { + blocks.getOrPut(position.x) { + TreeMap() + }.getOrPut(position.z) { + TreeMap() + }[position.y] = state + } +} diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockState.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockState.kt similarity index 65% rename from tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockState.kt rename to tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockState.kt index c2c9c0a..b05be16 100644 --- a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockState.kt +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockState.kt @@ -1,4 +1,4 @@ -package cloud.kubelet.foundation.gjallarhorn.render +package cloud.kubelet.foundation.gjallarhorn.state import kotlinx.serialization.Serializable diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockTrackMode.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockTrackMode.kt similarity index 54% rename from tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockTrackMode.kt rename to tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockTrackMode.kt index 0c757a5..c18785b 100644 --- a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/BlockTrackMode.kt +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockTrackMode.kt @@ -1,4 +1,4 @@ -package cloud.kubelet.foundation.gjallarhorn.render +package cloud.kubelet.foundation.gjallarhorn.state enum class BlockTrackMode { RemoveOnDelete,