From fc8e7128ed5b3d438f263c5ec03482cfd7b70db7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Przemys=C5=82aw=20Pluta?= Date: Thu, 4 Feb 2021 13:15:03 +0100 Subject: [PATCH] [Editor] Create working PoC of tile drawing --- .../command/service/DefaultUndoRedoService.kt | 13 +++- .../editor/controller/map/MapController.kt | 7 +-- .../controller/tileset/TileSetController.kt | 2 +- .../base/editor/model/map/layer/TileLayer.kt | 3 +- .../base/editor/model/map/map/GameMap.kt | 6 +- .../editor/render/canvas/map/MapCanvas.kt | 59 ++++++++++++++++++- .../editor/render/canvas/map/MapMouseEvent.kt | 16 +++++ .../render/canvas/map/MapPaintingTrace.kt | 56 ++++++++++++++++++ .../base/editor/view/component/map/MapPane.kt | 18 ++++-- .../base/editor/view/fragment/MapFragment.kt | 7 ++- .../base/editor/view/main/MainView.kt | 16 +++++ editor/src/main/resources/application.yml | 3 + 12 files changed, 185 insertions(+), 21 deletions(-) create mode 100755 editor/src/main/kotlin/com/bartlomiejpluta/base/editor/render/canvas/map/MapMouseEvent.kt create mode 100755 editor/src/main/kotlin/com/bartlomiejpluta/base/editor/render/canvas/map/MapPaintingTrace.kt create mode 100755 editor/src/main/resources/application.yml diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/service/DefaultUndoRedoService.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/service/DefaultUndoRedoService.kt index 4da0b061..f37f738a 100755 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/service/DefaultUndoRedoService.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/command/service/DefaultUndoRedoService.kt @@ -1,10 +1,11 @@ package com.bartlomiejpluta.base.editor.command.service import com.bartlomiejpluta.base.editor.command.model.Undoable -import org.springframework.stereotype.Service +import org.apache.commons.logging.LogFactory +import org.springframework.stereotype.Component import java.util.* -@Service +@Component class DefaultUndoRedoService : UndoRedoService { private val undo: Deque = ArrayDeque() private val redo: Deque = ArrayDeque() @@ -22,9 +23,11 @@ class DefaultUndoRedoService : UndoRedoService { override fun push(undoable: Undoable) { if(undo.size == sizeMax) { + log.debug("The max size of [undo] list has been reached. Removing the last item...") undo.removeLast() } + log.debug("Pushing item to [undo] list: ${undoable.commandName}") undo.push(undoable) redo.clear() } @@ -32,6 +35,7 @@ class DefaultUndoRedoService : UndoRedoService { override fun undo() { if(undo.isNotEmpty()) { undo.pop().let { + log.debug("Performing undo: ${it.commandName}") it.undo() redo.push(it) } @@ -41,6 +45,7 @@ class DefaultUndoRedoService : UndoRedoService { override fun redo() { if(redo.isNotEmpty()) { redo.pop().let { + log.debug("Performing redo: ${it.commandName}") it.redo() undo.push(it) } @@ -58,4 +63,8 @@ class DefaultUndoRedoService : UndoRedoService { override val redoCommandName: String get() = redo.last?.commandName ?: "" + + companion object { + private val log = LogFactory.getLog(DefaultUndoRedoService::class.java) + } } \ No newline at end of file diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/controller/map/MapController.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/controller/map/MapController.kt index 9a68cf51..7c02f5f7 100755 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/controller/map/MapController.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/controller/map/MapController.kt @@ -1,5 +1,6 @@ package com.bartlomiejpluta.base.editor.controller.map +import com.bartlomiejpluta.base.editor.command.service.UndoRedoService import com.bartlomiejpluta.base.editor.controller.tileset.TileSetController import com.bartlomiejpluta.base.editor.model.map.map.GameMap import org.springframework.stereotype.Component @@ -7,15 +8,11 @@ import tornadofx.Controller @Component class MapController : Controller() { + private val undoRedoService: UndoRedoService by inject() private val tileSetController: TileSetController by inject() private val map1 = GameMap(tileSetController.tileset, 20, 20) .createTileLayer(0) - .createTileLayer(3, 5) - .createTileLayer(3, 5) - .createTileLayer(3, 5) - .createTileLayer(3, 5) - .createTileLayer(3, 5) private val map2 = GameMap(tileSetController.tileset, 50, 50) .createTileLayer(3) diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/controller/tileset/TileSetController.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/controller/tileset/TileSetController.kt index 31fe5622..99e78e71 100755 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/controller/tileset/TileSetController.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/controller/tileset/TileSetController.kt @@ -1,6 +1,6 @@ package com.bartlomiejpluta.base.editor.controller.tileset -import com.bartlomiejpluta.base.editor.model.map.tileset.TileSet +import com.bartlomiejpluta.base.editor.model.tileset.TileSet import org.springframework.stereotype.Component import tornadofx.Controller diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/model/map/layer/TileLayer.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/model/map/layer/TileLayer.kt index 8e3271ff..ae759c6e 100755 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/model/map/layer/TileLayer.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/model/map/layer/TileLayer.kt @@ -1,7 +1,6 @@ package com.bartlomiejpluta.base.editor.model.map.layer -import com.bartlomiejpluta.base.editor.model.map.tileset.Tile -import javafx.scene.canvas.GraphicsContext +import com.bartlomiejpluta.base.editor.model.tileset.Tile class TileLayer(val layer: Array>) : Layer { diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/model/map/map/GameMap.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/model/map/map/GameMap.kt index 0bfdc0cd..fb5b3f97 100755 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/model/map/map/GameMap.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/model/map/map/GameMap.kt @@ -2,7 +2,7 @@ package com.bartlomiejpluta.base.editor.model.map.map import com.bartlomiejpluta.base.editor.model.map.layer.Layer import com.bartlomiejpluta.base.editor.model.map.layer.TileLayer -import com.bartlomiejpluta.base.editor.model.map.tileset.TileSet +import com.bartlomiejpluta.base.editor.model.tileset.TileSet class GameMap(val tileSet: TileSet, val rows: Int, val columns: Int) { @@ -39,8 +39,4 @@ class GameMap(val tileSet: TileSet, val rows: Int, val columns: Int) { fun setTile(layer: Int, row: Int, column: Int, tileRow: Int, tileColumn: Int) = apply { (layers[layer] as TileLayer).setTile(row, column, tileSet.getTile(tileRow, tileColumn)) } - - companion object { - val EMPTY_8x8 = GameMap(TileSet.EMPTY, 8, 8) - } } \ No newline at end of file diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/render/canvas/map/MapCanvas.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/render/canvas/map/MapCanvas.kt index b4a8e7f9..a05220d2 100755 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/render/canvas/map/MapCanvas.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/render/canvas/map/MapCanvas.kt @@ -3,10 +3,17 @@ package com.bartlomiejpluta.base.editor.render.canvas.map import com.bartlomiejpluta.base.editor.model.map.map.GameMap import com.bartlomiejpluta.base.editor.model.map.layer.Layer import com.bartlomiejpluta.base.editor.model.map.layer.TileLayer +import com.bartlomiejpluta.base.editor.model.tileset.TileSet import com.bartlomiejpluta.base.editor.render.model.Renderable import javafx.scene.canvas.GraphicsContext +import javafx.scene.input.MouseEvent +import javafx.scene.paint.Color +import org.apache.commons.logging.LogFactory -class MapCanvas : Renderable { + +class MapCanvas(private val paintingCallback: (MapPaintingTrace) -> Unit) : Renderable { + private var map: GameMap? = null + private var tileSet: TileSet? = null private var layers: List? = null private var rows: Int? = null private var columns: Int? = null @@ -15,7 +22,14 @@ class MapCanvas : Renderable { private var mapWidth: Double? = null private var mapHeight: Double? = null + private var mouseColumn = -1 + private var mouseRow = -1 + + private var currentTrace: MapPaintingTrace? = null + fun updateMap(gameMap: GameMap) { + map = gameMap + tileSet = gameMap.tileSet layers = gameMap.layers rows = gameMap.rows columns = gameMap.columns @@ -26,8 +40,11 @@ class MapCanvas : Renderable { } override fun render(gc: GraphicsContext) { + gc.clearRect(0.0, 0.0, gc.canvas.width, gc.canvas.height); + layers?.forEach { dispatchLayerRender(gc, it) } renderGrid(gc) + renderCursor(gc) } private fun dispatchLayerRender(gc: GraphicsContext, layer: Layer) { @@ -57,4 +74,44 @@ class MapCanvas : Renderable { gc.strokeLine(column * (tileWidth ?: 0.0), 0.0, column * (tileWidth ?: 0.0), (mapHeight ?: 0.0)) } } + + private fun renderCursor(gc: GraphicsContext) { + if(mouseColumn >= 0 && mouseRow >= 0) { + gc.fill = CURSOR_BRUSH + gc.fillRect(mouseColumn * (tileWidth ?: 0.0), mouseRow * (tileHeight ?: 0.0), tileWidth ?: 0.0, tileHeight ?: 0.0) + gc.fill = CURSOR_STROKE_BRUSH + gc.strokeRect(mouseColumn * (tileWidth ?: 0.0), mouseRow * (tileHeight ?: 0.0), tileWidth ?: 0.0, tileHeight ?: 0.0) + + } + } + + fun handleMouseInput(event: MapMouseEvent) { + mouseRow = event.row + mouseColumn = event.column + + val tile = tileSet?.getTile(24, 4) + + when(event.type) { + MouseEvent.MOUSE_PRESSED -> map?.let { + currentTrace = MapPaintingTrace(it, "Paint trace").apply { + paint(0, mouseRow, mouseColumn, tile) + } + } + + MouseEvent.MOUSE_DRAGGED -> currentTrace?.apply { + paint(0, mouseRow, mouseColumn, tile) + } + + MouseEvent.MOUSE_RELEASED -> currentTrace?.let { + paintingCallback(it) + currentTrace = null + } + } + } + + companion object { + val CURSOR_BRUSH = Color.color(0.7, 0.3, 0.8, 0.6) + val CURSOR_STROKE_BRUSH = Color.color(0.7, 0.3, 0.8, 1.0) + private val log = LogFactory.getLog(MapCanvas::class.java) + } } \ No newline at end of file diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/render/canvas/map/MapMouseEvent.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/render/canvas/map/MapMouseEvent.kt new file mode 100755 index 00000000..0671b3f5 --- /dev/null +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/render/canvas/map/MapMouseEvent.kt @@ -0,0 +1,16 @@ +package com.bartlomiejpluta.base.editor.render.canvas.map + +import com.bartlomiejpluta.base.editor.model.tileset.TileSet +import javafx.event.EventType +import javafx.scene.input.MouseEvent + +class MapMouseEvent(val row: Int, val column: Int, val type: EventType) { + + companion object { + fun of(event: MouseEvent, tileSet: TileSet) = MapMouseEvent( + (event.y / tileSet.tileHeight).toInt(), + (event.x / tileSet.tileWidth).toInt(), + event.eventType + ) + } +} \ No newline at end of file diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/render/canvas/map/MapPaintingTrace.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/render/canvas/map/MapPaintingTrace.kt new file mode 100755 index 00000000..37d8b8d9 --- /dev/null +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/render/canvas/map/MapPaintingTrace.kt @@ -0,0 +1,56 @@ +package com.bartlomiejpluta.base.editor.render.canvas.map + +import com.bartlomiejpluta.base.editor.command.model.Undoable +import com.bartlomiejpluta.base.editor.model.map.layer.TileLayer +import com.bartlomiejpluta.base.editor.model.map.map.GameMap +import com.bartlomiejpluta.base.editor.model.tileset.Tile + + +data class MapPaintingTrace(val map: GameMap, override val commandName: String) : Undoable { + private val trace = mutableListOf() + + fun paint(layerIndex: Int, row: Int, column: Int, tile: Tile?) { + if (row >= map.rows || column >= map.columns) { + return + } + + val layer = (map.layers[layerIndex] as TileLayer).layer + val formerTile = layer[row][column] + + if (trace.isEmpty()) { + trace += Element(layerIndex, row, column, formerTile, tile) + layer[row][column] = tile + return + } + + val tileAlreadyPainted = + trace.find { it.layerIndex == layerIndex && it.row == row && it.column == column } != null + + if (!tileAlreadyPainted) { + trace += Element(layerIndex, row, column, formerTile, tile) + layer[row][column] = tile + } + } + + override fun undo() { + trace.forEach { + (map.layers[it.layerIndex] as TileLayer).layer[it.row][it.column] = it.formerTile + } + } + + override fun redo() { + trace.forEach { + (map.layers[it.layerIndex] as TileLayer).layer[it.row][it.column] = it.tile + } + } + + companion object { + private data class Element( + val layerIndex: Int, + val row: Int, + val column: Int, + val formerTile: Tile?, + val tile: Tile? + ) + } +} \ No newline at end of file diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/view/component/map/MapPane.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/view/component/map/MapPane.kt index db6edea5..8617ab97 100755 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/view/component/map/MapPane.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/view/component/map/MapPane.kt @@ -1,24 +1,31 @@ package com.bartlomiejpluta.base.editor.view.component.map import com.bartlomiejpluta.base.editor.model.map.map.GameMap +import com.bartlomiejpluta.base.editor.model.tileset.TileSet import com.bartlomiejpluta.base.editor.render.canvas.map.MapCanvas +import com.bartlomiejpluta.base.editor.render.canvas.map.MapMouseEvent +import com.bartlomiejpluta.base.editor.render.canvas.map.MapPaintingTrace import javafx.event.EventHandler import javafx.scene.canvas.Canvas import javafx.scene.input.MouseEvent -class MapPane : Canvas(), EventHandler { - private val mapCanvas = MapCanvas() +class MapPane(paintingCallback: (MapPaintingTrace) -> Unit) : Canvas(), EventHandler { + private var tileSet: TileSet? = null + private val mapCanvas = MapCanvas(paintingCallback) init { onMouseMoved = this - onMouseClicked = this onMouseDragged = this + onMousePressed = this + onMouseReleased = this } fun updateMap(map: GameMap) { + tileSet = map.tileSet width = map.width.toDouble() height = map.height.toDouble() mapCanvas.updateMap(map) + render() } fun render() { @@ -26,7 +33,10 @@ class MapPane : Canvas(), EventHandler { } override fun handle(event: MouseEvent?) { - mapCanvas.handle(event) + if (event != null && tileSet != null) { + mapCanvas.handleMouseInput(MapMouseEvent.of(event, tileSet!!)) + } + mapCanvas.render(graphicsContext2D) } } \ No newline at end of file diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/view/fragment/MapFragment.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/view/fragment/MapFragment.kt index 7a87adae..9c261c91 100755 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/view/fragment/MapFragment.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/view/fragment/MapFragment.kt @@ -1,5 +1,6 @@ package com.bartlomiejpluta.base.editor.view.fragment +import com.bartlomiejpluta.base.editor.command.service.UndoRedoService import com.bartlomiejpluta.base.editor.model.map.map.GameMap import com.bartlomiejpluta.base.editor.view.component.map.MapPane import javafx.beans.property.SimpleDoubleProperty @@ -10,7 +11,8 @@ import tornadofx.plusAssign import tornadofx.scrollpane class MapFragment : Fragment() { - private val pane = MapPane() + private val undoRedoService: UndoRedoService by di() + private val pane = MapPane { undoRedoService.push(it) } val scaleProperty = SimpleDoubleProperty(1.0) private val transformation = Scale(1.0, 1.0, 0.0, 0.0).apply { @@ -20,6 +22,9 @@ class MapFragment : Fragment() { fun updateMap(map: GameMap) { pane.updateMap(map) + } + + fun redraw() { pane.render() } diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/view/main/MainView.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/view/main/MainView.kt index 5efcc1bb..e3ec6a4b 100755 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/view/main/MainView.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/view/main/MainView.kt @@ -1,10 +1,12 @@ package com.bartlomiejpluta.base.editor.view.main +import com.bartlomiejpluta.base.editor.command.service.UndoRedoService import com.bartlomiejpluta.base.editor.controller.map.MapController import com.bartlomiejpluta.base.editor.view.fragment.MapFragment import tornadofx.* class MainView : View() { + private val undoRedoService: UndoRedoService by di() private val mapController: MapController by di() private val mapFragment = find() @@ -33,6 +35,20 @@ class MainView : View() { mapFragment.scaleProperty.value -= 0.1 } } + + button("Undo") { + action { + undoRedoService.undo() + mapFragment.redraw() + } + } + + button("Redo") { + action { + undoRedoService.redo() + mapFragment.redraw() + } + } } center = mapFragment.root diff --git a/editor/src/main/resources/application.yml b/editor/src/main/resources/application.yml new file mode 100755 index 00000000..1753f5db --- /dev/null +++ b/editor/src/main/resources/application.yml @@ -0,0 +1,3 @@ +logging: + level: + com.bartlomiejpluta.base.editor: DEBUG \ No newline at end of file