From 6bcddb15b5f6d13eb175e363a9b56a1247543b1d Mon Sep 17 00:00:00 2001 From: Kenneth Endfinger Date: Sun, 20 Feb 2022 05:35:47 -0500 Subject: [PATCH] Gjallarhorn: First attempt at a graphical render system. --- .../commands/BlockChangeTimelapseCommand.kt | 2 +- .../commands/ChunkExportLoaderCommand.kt | 7 +++--- .../gjallarhorn/commands/ImageRenderType.kt | 10 ++++----- .../gjallarhorn/export/ChunkExportLoader.kt | 7 ++++-- .../render/LaunchGraphicalRenderSession.kt | 15 +++++++++++++ .../render/ui/GraphicalRenderSession.kt | 22 +++++++++++++++++++ .../render/ui/LazyImageRenderer.kt | 21 ++++++++++++++++++ .../gjallarhorn/state/BlockLogTracker.kt | 11 ++++------ .../foundation/gjallarhorn/util/numerics.kt | 10 +++++++-- 9 files changed, 84 insertions(+), 21 deletions(-) create mode 100644 tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/LaunchGraphicalRenderSession.kt create mode 100644 tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/ui/GraphicalRenderSession.kt create mode 100644 tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/ui/LazyImageRenderer.kt diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/commands/BlockChangeTimelapseCommand.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/commands/BlockChangeTimelapseCommand.kt index 532ce57..2066577 100644 --- a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/commands/BlockChangeTimelapseCommand.kt +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/commands/BlockChangeTimelapseCommand.kt @@ -104,7 +104,7 @@ class BlockChangeTimelapseCommand : CliktCommand("Block Change Timelapse", name changelog = changelog, blockTrackMode = if (considerAirBlocks) BlockTrackMode.AirOnDelete else BlockTrackMode.RemoveOnDelete, delegate = timelapse, - createRendererFunction = { expanse -> render.create(expanse, db) }, + createRendererFunction = { expanse -> render.createNewRenderer(expanse, db) }, threadPoolExecutor = threadPoolExecutor ) { slice, result -> val speed = slice.sliceRelativeDuration.toSeconds().toDouble() / timelapseMode.interval.toSeconds().toDouble() diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/commands/ChunkExportLoaderCommand.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/commands/ChunkExportLoaderCommand.kt index 0a0527f..55c10bc 100644 --- a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/commands/ChunkExportLoaderCommand.kt +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/commands/ChunkExportLoaderCommand.kt @@ -10,6 +10,7 @@ import com.github.ajalt.clikt.core.requireObject import com.github.ajalt.clikt.parameters.arguments.argument import com.github.ajalt.clikt.parameters.options.option import com.github.ajalt.clikt.parameters.types.enum +import com.github.ajalt.clikt.parameters.types.int import com.github.ajalt.clikt.parameters.types.path import org.jetbrains.exposed.sql.Database @@ -18,17 +19,17 @@ class ChunkExportLoaderCommand : CliktCommand("Chunk Export Loader", name = "chu private val exportDirectoryPath by argument("export-directory-path").path() private val world by argument("world") - + private val chunkLoadLimit by option("--chunk-limit", help = "Chunk Limit").int() private val render by option("--render", help = "Render Top Down Image").enum { it.id } override fun run() { val tracker = BlockLogTracker(isConcurrent = true) val loader = ChunkExportLoader(tracker = tracker) - loader.loadAllChunksForWorld(exportDirectoryPath, world, fast = true) + loader.loadAllChunksForWorld(exportDirectoryPath, world, fast = true, limit = chunkLoadLimit) if (render != null) { val expanse = BlockExpanse.zeroOffsetAndMax(tracker.calculateZeroBlockOffset(), tracker.calculateMaxBlock()) val map = tracker.buildBlockMap(expanse.offset) - val renderer = render!!.create(expanse, db) + val renderer = render!!.createNewRenderer(expanse, db) val image = renderer.render(ChangelogSlice.none, map) image.savePngFile("full.png") } diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/commands/ImageRenderType.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/commands/ImageRenderType.kt index 2ff5136..7d86f8a 100644 --- a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/commands/ImageRenderType.kt +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/commands/ImageRenderType.kt @@ -1,18 +1,16 @@ 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.render.PlayerLocationShareRenderer +import cloud.kubelet.foundation.gjallarhorn.render.* import cloud.kubelet.foundation.gjallarhorn.state.BlockExpanse import org.jetbrains.exposed.sql.Database @Suppress("unused") enum class ImageRenderType( val id: String, - val create: (BlockExpanse, Database) -> BlockImageRenderer + val createNewRenderer: (BlockExpanse, Database) -> BlockImageRenderer ) { BlockDiversity("block-diversity", { expanse, _ -> BlockDiversityRenderer(expanse) }), HeightMap("height-map", { expanse, _ -> BlockHeightMapRenderer(expanse) }), - PlayerPosition("player-position", { expanse, db -> PlayerLocationShareRenderer(expanse, db) }) + PlayerPosition("player-position", { expanse, db -> PlayerLocationShareRenderer(expanse, db) }), + GraphicalSession("graphical", { expanse, _ -> LaunchGraphicalRenderSession(expanse) }) } diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/export/ChunkExportLoader.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/export/ChunkExportLoader.kt index 2b60460..5616c7d 100644 --- a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/export/ChunkExportLoader.kt +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/export/ChunkExportLoader.kt @@ -14,8 +14,11 @@ import kotlin.io.path.inputStream import kotlin.io.path.listDirectoryEntries class ChunkExportLoader(val map: SparseBlockStateMap? = null, val tracker: BlockLogTracker? = null) { - fun loadAllChunksForWorld(path: Path, world: String, fast: Boolean = false) { - val chunkFiles = path.listDirectoryEntries("${world}_chunk_*.json.gz") + fun loadAllChunksForWorld(path: Path, world: String, fast: Boolean = false, limit: Int? = null) { + var chunkFiles = path.listDirectoryEntries("${world}_chunk_*.json.gz") + if (limit != null) { + chunkFiles = chunkFiles.take(limit) + } if (fast) { chunkFiles.parallelStream().forEach { loadChunkFile(it, id = chunkFiles.indexOf(it)) } } else { diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/LaunchGraphicalRenderSession.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/LaunchGraphicalRenderSession.kt new file mode 100644 index 0000000..8da97b9 --- /dev/null +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/LaunchGraphicalRenderSession.kt @@ -0,0 +1,15 @@ +package cloud.kubelet.foundation.gjallarhorn.render + +import cloud.kubelet.foundation.gjallarhorn.render.ui.GraphicalRenderSession +import cloud.kubelet.foundation.gjallarhorn.state.BlockExpanse +import cloud.kubelet.foundation.gjallarhorn.state.BlockStateMap +import cloud.kubelet.foundation.gjallarhorn.state.ChangelogSlice +import java.awt.image.BufferedImage + +class LaunchGraphicalRenderSession(val expanse: BlockExpanse) : BlockImageRenderer { + override fun render(slice: ChangelogSlice, map: BlockStateMap): BufferedImage { + val session = GraphicalRenderSession(expanse, map) + session.isVisible = true + return BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR) + } +} diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/ui/GraphicalRenderSession.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/ui/GraphicalRenderSession.kt new file mode 100644 index 0000000..9481ad0 --- /dev/null +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/ui/GraphicalRenderSession.kt @@ -0,0 +1,22 @@ +package cloud.kubelet.foundation.gjallarhorn.render.ui + +import cloud.kubelet.foundation.gjallarhorn.render.BlockDiversityRenderer +import cloud.kubelet.foundation.gjallarhorn.render.BlockHeightMapRenderer +import cloud.kubelet.foundation.gjallarhorn.render.BlockVerticalFillMapRenderer +import cloud.kubelet.foundation.gjallarhorn.state.BlockExpanse +import cloud.kubelet.foundation.gjallarhorn.state.BlockStateMap +import java.awt.Dimension +import javax.swing.JFrame +import javax.swing.JTabbedPane + +class GraphicalRenderSession(val expanse: BlockExpanse, val map: BlockStateMap) : JFrame() { + init { + name = "Gjallarhorn Renderer" + size = Dimension(1024, 1024) + val pane = JTabbedPane() + pane.addTab("Block Diversity", LazyImageRenderer(map, BlockDiversityRenderer(expanse))) + pane.addTab("Height Map", LazyImageRenderer(map, BlockHeightMapRenderer(expanse))) + pane.addTab("Vertical Fill Map", LazyImageRenderer(map, BlockVerticalFillMapRenderer(expanse))) + add(pane) + } +} diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/ui/LazyImageRenderer.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/ui/LazyImageRenderer.kt new file mode 100644 index 0000000..43a00a3 --- /dev/null +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/render/ui/LazyImageRenderer.kt @@ -0,0 +1,21 @@ +package cloud.kubelet.foundation.gjallarhorn.render.ui + +import cloud.kubelet.foundation.gjallarhorn.render.BlockImageRenderer +import cloud.kubelet.foundation.gjallarhorn.state.BlockStateMap +import cloud.kubelet.foundation.gjallarhorn.state.ChangelogSlice +import java.awt.Graphics +import javax.swing.JComponent + +class LazyImageRenderer(val map: BlockStateMap, private val renderer: BlockImageRenderer) : JComponent() { + private val image by lazy { + renderer.render(ChangelogSlice.none, map) + } + + override fun paint(g: Graphics?) { + g?.drawImage(image, 0, 0, this) + } + + override fun paintComponent(g: Graphics?) { + g?.drawImage(image, 0, 0, this) + } +} diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockLogTracker.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockLogTracker.kt index c1a8437..9d478e1 100644 --- a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockLogTracker.kt +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/state/BlockLogTracker.kt @@ -1,5 +1,7 @@ package cloud.kubelet.foundation.gjallarhorn.state +import cloud.kubelet.foundation.gjallarhorn.util.maxOfAll +import cloud.kubelet.foundation.gjallarhorn.util.minOfAll import java.util.concurrent.ConcurrentHashMap import kotlin.math.absoluteValue @@ -23,10 +25,7 @@ class BlockLogTracker(private val mode: BlockTrackMode = BlockTrackMode.RemoveOn } fun calculateZeroBlockOffset(): BlockCoordinate { - val x = blocks.keys.minOf { it.x } - val y = blocks.keys.minOf { it.y } - val z = blocks.keys.minOf { it.z } - + val (x, y, z) = blocks.keys.minOfAll(3) { listOf(it.x, it.y, it.z) } val xOffset = if (x < 0) x.absoluteValue else 0 val yOffset = if (y < 0) y.absoluteValue else 0 val zOffset = if (z < 0) z.absoluteValue else 0 @@ -35,9 +34,7 @@ class BlockLogTracker(private val mode: BlockTrackMode = BlockTrackMode.RemoveOn } fun calculateMaxBlock(): BlockCoordinate { - val x = blocks.keys.maxOf { it.x } - val y = blocks.keys.maxOf { it.y } - val z = blocks.keys.maxOf { it.z } + val (x, y, z) = blocks.keys.maxOfAll(3) { listOf(it.x, it.y, it.z) } return BlockCoordinate(x, y, z) } diff --git a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/util/numerics.kt b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/util/numerics.kt index 9528de0..9ca1985 100644 --- a/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/util/numerics.kt +++ b/tool-gjallarhorn/src/main/kotlin/cloud/kubelet/foundation/gjallarhorn/util/numerics.kt @@ -1,6 +1,6 @@ package cloud.kubelet.foundation.gjallarhorn.util -fun Sequence.minOfAll(fieldCount: Int, block: (value: T) -> List): List { +fun Iterable.minOfAll(fieldCount: Int, block: (value: T) -> List): List { val fieldRange = 0 until fieldCount val results = fieldRange.map { Long.MAX_VALUE }.toMutableList() for (item in this) { @@ -16,7 +16,7 @@ fun Sequence.minOfAll(fieldCount: Int, block: (value: T) -> List): return results } -fun Sequence.maxOfAll(fieldCount: Int, block: (value: T) -> List): List { +fun Iterable.maxOfAll(fieldCount: Int, block: (value: T) -> List): List { val fieldRange = 0 until fieldCount val results = fieldRange.map { Long.MIN_VALUE }.toMutableList() for (item in this) { @@ -31,3 +31,9 @@ fun Sequence.maxOfAll(fieldCount: Int, block: (value: T) -> List): } return results } + +fun Sequence.minOfAll(fieldCount: Int, block: (value: T) -> List): List = + asIterable().minOfAll(fieldCount, block) + +fun Sequence.maxOfAll(fieldCount: Int, block: (value: T) -> List): List = + asIterable().maxOfAll(fieldCount, block)