summaryrefslogtreecommitdiff
path: root/app/src/main/java/space/jakob/mines/GameView.kt
diff options
context:
space:
mode:
authorJakob L. Kreuze <jakob@memeware.net>2018-12-21 12:38:37 -0500
committerJakob L. Kreuze <jakob@memeware.net>2018-12-21 12:38:37 -0500
commitca1a389e97e3eaa17632e419891eb4514ea651e0 (patch)
treec81a52f326f5669b6cab516c96f3874e6af5aa03 /app/src/main/java/space/jakob/mines/GameView.kt
parent30d17197c169d7dc1c8b210e6c43fbfd698cdad6 (diff)
Redesigned the algorithm for drawing a portion of the minesweeper grid
Diffstat (limited to 'app/src/main/java/space/jakob/mines/GameView.kt')
-rw-r--r--app/src/main/java/space/jakob/mines/GameView.kt113
1 files changed, 73 insertions, 40 deletions
diff --git a/app/src/main/java/space/jakob/mines/GameView.kt b/app/src/main/java/space/jakob/mines/GameView.kt
index 36fe2ec..52a8283 100644
--- a/app/src/main/java/space/jakob/mines/GameView.kt
+++ b/app/src/main/java/space/jakob/mines/GameView.kt
@@ -22,8 +22,8 @@ import android.util.AttributeSet
import android.view.View
import android.util.Log
-const val atlasWidth = 3
-const val tileSpacing = 2.0 / 27.0
+import kotlin.math.ceil
+import kotlin.math.floor
enum class AtlasEntry {
BLANK, MASKED, UNMASKED,
@@ -35,52 +35,47 @@ class GameView(context: Context, attrs: AttributeSet) : View(context, attrs) {
val atlas = BitmapFactory.decodeResource(context.resources, R.drawable.atlas);
val tileWidth = atlas.width / 3
+ // FIXME: Is this correct? `breadth` tiles, excluding halfsies and such?
+ val scaledTileWidth: Int
+ get() = (minOf(width, height) / cameraBreadth).toInt()
+
var grid: Grid? = null
+ // All of the state for maintaining the view's current positioning. For an
+ // explanation of these values, see `drawGrid`.
var cameraX = 0.0
var cameraY = 0.0
- var sightX = 5.0
- var sightY = 5.0
-
- val maxCameraX: Double
- get() = grid?.let {
- it.width - sightX
- } ?: 0.0
- val maxCameraY: Double
- get() = grid?.let {
- it.height - sightY
- } ?: 0.0
-
- // The size, in pixels, of an individual tile based on the current scale
- // factor.
- val scaledWidth: Int
- get() = width / sightX.toInt()
- val scaledHeight: Int
- get() = height / sightY.toInt()
+ var cameraBreadth = 5.0
+ // TODO: Break up into multiple expressions. This isn't readable.
/**
* Returns the grid coordinates for the tile at the given screen coordinates.
*/
fun toGridCoordinates(x: Int, y: Int) = Pair(
- (x + (scaledWidth * (cameraX % 1)).toInt()) / scaledWidth + cameraX.toInt(),
- (y + (scaledHeight * (cameraY % 1)).toInt()) / scaledHeight + cameraY.toInt()
+ (x + (scaledTileWidth * (cameraX % 1)).toInt()) / scaledTileWidth + cameraX.toInt(),
+ (y + (scaledTileWidth * (cameraY % 1)).toInt()) / scaledTileWidth + cameraY.toInt()
)
private fun atlasRect(entry: AtlasEntry): Rect {
+ val atlasWidth = 3
+
val x = (entry.ordinal % atlasWidth) * tileWidth
val y = (entry.ordinal / atlasWidth) * tileWidth
return Rect(x, y, x + tileWidth, y + tileWidth)
}
private fun drawTile(canvas: Canvas, tile: Tile, x: Int, y: Int) {
- val xSpacing = (scaledWidth * tileSpacing).toInt()
- val ySpacing = (scaledWidth * tileSpacing).toInt()
+ val tileSpacing = 2.0 / 27.0
+ val xSpacing = (scaledTileWidth * tileSpacing).toInt()
+ val ySpacing = (scaledTileWidth * tileSpacing).toInt()
+
+ // TODO: Opportunity for more ASCII art explanations :)
val dst = Rect(
x + xSpacing,
y + ySpacing,
- x + scaledWidth - xSpacing,
- y + scaledHeight - ySpacing
+ x + scaledTileWidth - xSpacing,
+ y + scaledTileWidth - ySpacing
)
if (tile.masked) {
@@ -106,24 +101,62 @@ class GameView(context: Context, attrs: AttributeSet) : View(context, attrs) {
}
private fun drawGrid(canvas: Canvas, grid: Grid) {
- val startX = cameraX.toInt() // Truncates the fractional component.
- val startY = cameraY.toInt()
- val howManyX = (sightX.toInt() + 1).coerceAtMost(grid.width - startX - 1)
- val howManyY = (sightY.toInt() + 1).coerceAtMost(grid.height - startY - 1)
+ // The state maintained for drawing the view is a position, (`cameraX`,
+ // `cameraY`), representing the centroid of a rectangle whose sides are
+ // of length `cameraBreadthX` and `cameraBreadthY`. Only the tiles
+ // intersecting this rectangle are drawn.
+ //
+ // +============+
+ // / | |
+ // | | |
+ // | | |
+ // cameraBreadthY < | x |
+ // | | \__________ (cameraX, cameraY)
+ // | | |
+ // \ | |
+ // +============+
+ // \_____ ______/
+ // v
+ // cameraBreadthX
+
+ val cameraBreadthX = if (width > height) {
+ cameraBreadth * width.toDouble() / height.toDouble()
+ } else {
+ cameraBreadth
+ }
+ val cameraBreadthY = if (height > width) {
+ cameraBreadth * height.toDouble() / width.toDouble()
+ } else {
+ cameraBreadth
+ }
+
+ // This is where the fractional component comes into play -- drawing
+ // tiles that are partially obscured by the limited reaches of the
+ // camera view.
+ val offsetX = (scaledTileWidth * ((cameraX + (cameraBreadthX / 2)) % 1)).toInt()
+ val offsetY = (scaledTileWidth * ((cameraY + (cameraBreadthY / 2)) % 1) / 2).toInt()
+
+ val howManyX = ceil(cameraBreadthX).toInt()
+ val howManyY = ceil(cameraBreadthY).toInt()
+
+ val startX = cameraX - (cameraBreadthX / 2)
+ val startY = cameraY - (cameraBreadthY / 2)
for (i in 0..howManyX) {
for (j in 0..howManyY) {
- val tile = grid[startX + i, startY + j]
-
- val xOffset = (scaledWidth * (cameraX % 1)).toInt()
- val yOffset = (scaledHeight * (cameraY % 1)).toInt()
+ // The fractional components here are truncated, giving a
+ // equivalent indices into `grid`. Of course, these indices
+ // aren't necessarily within the bounds of the grid. If a tile
+ // doesn't exist, we'll just draw blank space.
+ val x = (startX + i.toDouble()).toInt()
+ val y = (startY + j.toDouble()).toInt()
- drawTile(
- canvas,
- tile,
- i * scaledWidth - xOffset,
- j * scaledHeight - yOffset
- )
+ if (grid.valid(x, y)) {
+ val tile = grid[x, y]
+ val screenX = i * scaledTileWidth
+ val screenY = j * scaledTileWidth
+ drawTile(canvas, tile, screenX + offsetX, screenY + offsetY)
+ }
}
}
}