root.imageSpaces

This commit is contained in:
DBotThePony 2023-03-27 18:11:01 +07:00
parent ca23dd2b95
commit 22c39ba497
Signed by: DBot
GPG Key ID: DCC23B5715498507
4 changed files with 116 additions and 13 deletions

View File

@ -3,5 +3,6 @@ package ru.dbotthepony.kstarbound
const val METRES_IN_STARBOUND_UNIT = 0.5
const val METRES_IN_STARBOUND_UNITf = 0.5f
const val PIXELS_IN_STARBOUND_UNIT = 8.0
const val PIXELS_IN_STARBOUND_UNITf = 8.0f
const val PIXELS_IN_STARBOUND_UNITi = 8
const val PIXELS_IN_STARBOUND_UNIT = PIXELS_IN_STARBOUND_UNITi.toDouble()
const val PIXELS_IN_STARBOUND_UNITf = PIXELS_IN_STARBOUND_UNITi.toFloat()

View File

@ -375,7 +375,18 @@ class Starbound : ISBFileLocator {
state.setTableFunction("imageSpaces", this) { args ->
// List<Vec2I> root.imageSpaces(String imagePath, Vec2F worldPosition, float spaceScan, bool flip)
TODO()
val values = imageData(args.getString()).worldSpaces(args.getVector2i(), args.getDouble(), args.getBool())
args.lua.pushTable(arraySize = values.size)
val table = args.lua.stackTop
for ((i, value) in values.withIndex()) {
args.lua.push(i)
args.lua.push(value)
args.lua.setTableValue(table)
}
1
}
state.setTableFunction("nonEmptyRegion", this) { args ->

View File

@ -2,7 +2,8 @@ package ru.dbotthepony.kstarbound.io
import org.lwjgl.stb.STBImage
import org.lwjgl.system.MemoryUtil.memAddress
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNIT
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITi
import ru.dbotthepony.kvector.vector.nint.Vector2i
import ru.dbotthepony.kvector.vector.nint.Vector4i
import java.lang.ref.Cleaner
@ -45,14 +46,65 @@ class ImageData(val data: ByteBuffer, val width: Int, val height: Int, val amoun
}
}
fun worldSpaces(pos: Vector2d, spaceScan: Double, flip: Boolean): List<Vector2i> {
when (amountOfChannels) {
3 -> TODO()
4 -> TODO()
else -> throw IllegalStateException("Can not check world space taken by image with $amountOfChannels color channels")
operator fun get(x: Int, y: Int, flip: Boolean): Int {
require(x in 0 until width) { "X out of bounds: $x" }
require(y in 0 until height) { "Y out of bounds: $y" }
if (flip) {
return this[width - x - 1, y]
} else {
return this[x, y]
}
}
fun isTransparent(x: Int, y: Int, flip: Boolean): Boolean {
if (x !in 0 until width) return true
if (y !in 0 until height) return true
if (amountOfChannels != 4) return false
return this[x, y, flip] and 0xFF != 0x0
}
fun worldSpaces(pixelOffset: Vector2i, spaceScan: Double, flip: Boolean): List<Vector2i> {
if (amountOfChannels != 3 && amountOfChannels != 4) throw IllegalStateException("Can not check world space taken by image with $amountOfChannels color channels")
val xDivL = pixelOffset.x % PIXELS_IN_STARBOUND_UNITi
val yDivB = pixelOffset.y % PIXELS_IN_STARBOUND_UNITi
val xDivR = (pixelOffset.x + width) % PIXELS_IN_STARBOUND_UNITi
val yDivT = (pixelOffset.y + height) % PIXELS_IN_STARBOUND_UNITi
val leftMostX = pixelOffset.x / PIXELS_IN_STARBOUND_UNITi - (if (xDivL != 0) 1 else 0)
val bottomMostY = pixelOffset.y / PIXELS_IN_STARBOUND_UNITi - (if (yDivB != 0) 1 else 0)
val rightMostX = (pixelOffset.x + width) / PIXELS_IN_STARBOUND_UNITi + (if (xDivR != 0) 1 else 0)
val topMostY = (pixelOffset.y + height) / PIXELS_IN_STARBOUND_UNITi + (if (yDivT != 0) 1 else 0)
val result = ArrayList<Vector2i>()
for (y in bottomMostY .. topMostY) {
for (x in leftMostX .. rightMostX) {
val left = x * PIXELS_IN_STARBOUND_UNITi
val bottom = y * PIXELS_IN_STARBOUND_UNITi
var transparentPixels = 0
for (sX in 0 until PIXELS_IN_STARBOUND_UNITi) {
for (sY in 0 until PIXELS_IN_STARBOUND_UNITi) {
if (isTransparent(xDivL + sX + left, yDivB + sY + bottom, flip)) {
transparentPixels++
}
}
}
if (transparentPixels * FILL_RATIO >= spaceScan) {
result.add(Vector2i(x, y))
}
}
}
return result
}
val nonEmptyRegion by lazy {
if (amountOfChannels == 4) {
var x0 = 0
@ -60,7 +112,7 @@ class ImageData(val data: ByteBuffer, val width: Int, val height: Int, val amoun
search@for (y in 0 until height) {
for (x in 0 until width) {
if (this[x, y] and 0x000000FF != 0x0) {
if (this[x, y] and 0xFF != 0x0) {
x0 = x
y0 = y
break@search
@ -73,7 +125,7 @@ class ImageData(val data: ByteBuffer, val width: Int, val height: Int, val amoun
search@for (y in height - 1 downTo y0) {
for (x in width - 1 downTo x0) {
if (this[x, y] and 0x000000FF != 0x0) {
if (this[x, y] and 0xFF != 0x0) {
x1 = x
y1 = y
break@search
@ -86,4 +138,8 @@ class ImageData(val data: ByteBuffer, val width: Int, val height: Int, val amoun
Vector4i(0, 0, width, height)
}
companion object {
const val FILL_RATIO = 1 / (PIXELS_IN_STARBOUND_UNIT * PIXELS_IN_STARBOUND_UNIT)
}
}

View File

@ -311,6 +311,29 @@ class LuaState private constructor(private val pointer: Pointer, val stringInter
return pairs
}
fun getVector2i(stackIndex: Int = -1): Vector2i? {
val abs = this.absStackIndex(stackIndex)
if (!this.isTable(abs))
return null
push(1)
loadTableValue(abs)
val x = getLong(abs + 1)
pop()
x ?: return null
push(2)
loadTableValue(abs)
val y = getLong(abs + 1)
pop()
y ?: return null
return Vector2i(x.toInt(), y.toInt())
}
/**
* Пропуски заполняются [JsonNull.INSTANCE]
*
@ -354,13 +377,13 @@ class LuaState private constructor(private val pointer: Pointer, val stringInter
return this.getValue(limit = limit)
}
fun loadTableValue(stackIndex: Int = -2) {
fun loadTableValue(stackIndex: Int = -2, allowNothing: Boolean = false) {
val abs = this.absStackIndex(stackIndex)
if (!this.isTable(abs))
throw IllegalArgumentException("Attempt to index an ${this.typeAt(abs)} value")
if (LuaJNR.INSTANCE.lua_gettable(this.pointer, abs) == LUA_TNONE)
if (LuaJNR.INSTANCE.lua_gettable(this.pointer, abs) == LUA_TNONE && !allowNothing)
throw IllegalStateException("loaded TNONE from Lua table")
}
@ -508,6 +531,18 @@ class LuaState private constructor(private val pointer: Pointer, val stringInter
?: throw IllegalArgumentException("Lua code error: Bad argument #$position: double expected, got ${this@LuaState.typeAt(position)}")
}
fun getInt(position: Int = this.position++): Int {
check(position in 1 ..this.top) { "JVM code error: Invalid argument position: $position" }
return this@LuaState.getLong(position)?.toInt()
?: throw IllegalArgumentException("Lua code error: Bad argument #$position: integer expected, got ${this@LuaState.typeAt(position)}")
}
fun getVector2i(position: Int = this.position++): Vector2i {
check(position in 1 ..this.top) { "JVM code error: Invalid argument position: $position" }
return this@LuaState.getVector2i(position)
?: throw IllegalArgumentException("Lua code error: Bad argument #$position: Vector2i expected, got ${this@LuaState.typeAt(position)}")
}
fun getBoolOrNil(position: Int = this.position++): Boolean? {
check(position in 1 ..this.top) { "JVM code error: Invalid argument position: $position" }