v0.25
This commit is contained in:
@@ -11,7 +11,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("me.alex_s168:blitz:0.24.1")
|
||||
implementation("me.alex_s168:blitz:0.25")
|
||||
}
|
||||
```
|
||||
|
||||
|
@@ -2,10 +2,11 @@ plugins {
|
||||
kotlin("jvm") version "1.9.21"
|
||||
application
|
||||
`maven-publish`
|
||||
id("org.jetbrains.dokka") version "2.0.0"
|
||||
}
|
||||
|
||||
group = "me.alex_s168"
|
||||
version = "0.24.1"
|
||||
version = "0.25"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
@@ -1,5 +1,7 @@
|
||||
package blitz
|
||||
|
||||
import blitz.collections.RefVec
|
||||
|
||||
fun interface DeferScope {
|
||||
fun defer(block: () -> Unit)
|
||||
}
|
||||
@@ -28,9 +30,9 @@ interface ExecutionScope: DeferScope {
|
||||
}
|
||||
|
||||
fun <R> resourceScoped(block: ExecutionScope.() -> R): R {
|
||||
val defer = mutableListOf<() -> Unit>()
|
||||
val onError = mutableListOf<() -> Unit>()
|
||||
val scope = ExecutionScope.create(defer::add, onError::add) {
|
||||
val defer = RefVec<() -> Unit>()
|
||||
val onError = RefVec<() -> Unit>()
|
||||
val scope = ExecutionScope.create(defer::pushBack, onError::pushBack) {
|
||||
throw Exception("Manual error triggered")
|
||||
}
|
||||
val ex: Exception
|
||||
|
@@ -1,14 +1,19 @@
|
||||
package blitz
|
||||
|
||||
import blitz.annotations.DontAccessManually
|
||||
|
||||
@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
|
||||
class Either<A: Any, B: Any> private constructor(
|
||||
aIn: A?, bIn: B?
|
||||
) {
|
||||
/** DO NOT SET MANUALLY!!! */
|
||||
@JvmField
|
||||
@DontAccessManually
|
||||
var a: A? = aIn
|
||||
|
||||
/** DO NOT SET MANUALLY!!! */
|
||||
@JvmField
|
||||
@DontAccessManually
|
||||
var b: B? = bIn
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
@@ -42,7 +47,10 @@ class Either<A: Any, B: Any> private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun <A: Any, B: Any> Either<A, B>.isA() = a != null
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun <A: Any, B: Any> Either<A, B>.isB() = b != null
|
||||
|
||||
inline fun <A: Any, B: Any> Either<A, B>.getAOr(prov: Provider<A>): A =
|
||||
|
@@ -3,17 +3,30 @@ package blitz
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
|
||||
enum class Endian {
|
||||
LITTLE,
|
||||
BIG
|
||||
;
|
||||
@JvmInline
|
||||
value class Endian private constructor(val little: Boolean) {
|
||||
companion object {
|
||||
@Deprecated("Renamed", replaceWith = ReplaceWith("Little"))
|
||||
val LITTLE = Endian(little = true)
|
||||
@JvmStatic
|
||||
val Little = Endian(little = true)
|
||||
|
||||
infix fun encodeLittle(little: ByteArray) =
|
||||
if (this == BIG) little.reversedArray()
|
||||
@Deprecated("Renamed", replaceWith = ReplaceWith("Big"))
|
||||
val BIG = Endian(little = false)
|
||||
@JvmStatic
|
||||
val Big = Endian(little = false)
|
||||
}
|
||||
|
||||
inline val big get() = !little
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline infix fun encodeLittle(little: ByteArray) =
|
||||
if (this.big) little.reversedArray()
|
||||
else little
|
||||
|
||||
fun toNIO(): ByteOrder =
|
||||
if (this == LITTLE) ByteOrder.LITTLE_ENDIAN
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun toNIO(): ByteOrder =
|
||||
if (this.little) ByteOrder.LITTLE_ENDIAN
|
||||
else ByteOrder.BIG_ENDIAN
|
||||
}
|
||||
|
||||
@@ -22,8 +35,12 @@ fun ByteBuffer.order(endian: Endian): ByteBuffer =
|
||||
|
||||
fun Long.toBytes(endian: Endian) =
|
||||
endian encodeLittle
|
||||
toInt().toBytes(Endian.LITTLE) +
|
||||
toInt().shr(32).toBytes(Endian.LITTLE)
|
||||
toInt().toBytes(Endian.Little) +
|
||||
toInt().shr(32).toBytes(Endian.Little)
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun ULong.toBytes(endian: Endian) =
|
||||
toLong().toBytes(endian)
|
||||
|
||||
fun Int.toBytes(endian: Endian) =
|
||||
endian encodeLittle byteArrayOf(
|
||||
@@ -33,19 +50,22 @@ fun Int.toBytes(endian: Endian) =
|
||||
this.shr(24).and(0xFF).toByte()
|
||||
)
|
||||
|
||||
fun UInt.toBytes(endian: Endian) =
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun UInt.toBytes(endian: Endian) =
|
||||
toInt().toBytes(endian)
|
||||
|
||||
fun Short.toBytes(endian: Endian) =
|
||||
endian encodeLittle toInt().toBytes(Endian.LITTLE).copyOf(2)
|
||||
endian encodeLittle toInt().toBytes(Endian.Little).copyOf(2)
|
||||
|
||||
fun UShort.toBytes(endian: Endian) =
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun UShort.toBytes(endian: Endian) =
|
||||
toShort().toBytes(endian)
|
||||
|
||||
fun Byte.toBytes() =
|
||||
byteArrayOf(this)
|
||||
|
||||
fun UByte.toBytes() =
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun UByte.toBytes() =
|
||||
toByte().toBytes()
|
||||
|
||||
// TODO: no cheat
|
||||
@@ -68,8 +88,10 @@ fun ByteArray.toLong(endian: Endian) =
|
||||
fun ByteArray.toULong(endian: Endian) =
|
||||
toLong(endian).toULong()
|
||||
|
||||
fun ByteArray.toByte() =
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun ByteArray.toByte() =
|
||||
this[0]
|
||||
|
||||
fun ByteArray.toUByte() =
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun ByteArray.toUByte() =
|
||||
this[0].toUByte()
|
@@ -1,13 +0,0 @@
|
||||
package blitz
|
||||
|
||||
@Deprecated(
|
||||
level = DeprecationLevel.ERROR,
|
||||
message = "Will be removed in the future!",
|
||||
replaceWith = ReplaceWith(
|
||||
"Terminal.warn",
|
||||
"blitz.term.Terminal"
|
||||
)
|
||||
)
|
||||
fun warn(msg: String) {
|
||||
System.err.println(msg)
|
||||
}
|
@@ -1,87 +0,0 @@
|
||||
package blitz
|
||||
|
||||
import blitz.collections.selfInitializingSequence
|
||||
|
||||
class OperationChain<I, O> private constructor(
|
||||
private val impl: Impl = Impl()
|
||||
) {
|
||||
private var until = 0
|
||||
|
||||
private class Impl {
|
||||
val seqe = mutableListOf<(Sequence<Any?>) -> Sequence<Any?>>()
|
||||
|
||||
var finalized = false
|
||||
|
||||
fun add(op: Operator<*, *>) {
|
||||
seqe += { seq: Sequence<Any?> ->
|
||||
seq.map(op as Operator<Any?, Any?>)
|
||||
}
|
||||
}
|
||||
|
||||
fun addFlat(op: Operator<*, Sequence<*>>) {
|
||||
seqe += { seq: Sequence<Any?> ->
|
||||
seq.flatMap(op as Operator<Any?, Sequence<Any?>>)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <NO> map(op: Operator<O, NO>): OperationChain<I, NO> =
|
||||
OperationChain<I, NO>(impl.also { it.add(op) })
|
||||
.also { it.until = this.until + 1 }
|
||||
|
||||
fun <NO> flatMap(op: Operator<O, Sequence<NO>>): OperationChain<I, NO> =
|
||||
OperationChain<I, NO>(impl.also { it.addFlat(op) })
|
||||
.also { it.until = this.until + 1 }
|
||||
|
||||
fun <NO> map(op: OperationChain<O, NO>): OperationChain<I, NO> {
|
||||
if (!op.impl.finalized)
|
||||
throw Exception("Can not map un-finalized operation chain onto operation chain!")
|
||||
return flatMap(op::process)
|
||||
}
|
||||
|
||||
fun <NO> modifier(op: Operator<Sequence<O>, Sequence<NO>>): OperationChain<I, NO> =
|
||||
OperationChain<I, NO>(impl.also { it.seqe.add(op as (Sequence<Any?>) -> Sequence<Any?>) })
|
||||
.also { it.until = this.until + 1 }
|
||||
|
||||
fun finalize(): OperationChain<I, O> {
|
||||
if (impl.finalized)
|
||||
throw Exception("Can't finalize a finalized OperationChain!")
|
||||
impl.finalized = true
|
||||
return this
|
||||
}
|
||||
|
||||
fun process(v: I): Sequence<O> =
|
||||
selfInitializingSequence {
|
||||
var seq = sequenceOf<Any?>(v)
|
||||
impl.seqe
|
||||
.asSequence()
|
||||
.take(until)
|
||||
.forEach { op ->
|
||||
seq = op(seq)
|
||||
}
|
||||
seq as Sequence<O>
|
||||
}
|
||||
|
||||
fun processAll(v: Sequence<I>): Sequence<O> =
|
||||
v.flatMap { process(it) }
|
||||
|
||||
companion object {
|
||||
internal fun <I> create(): OperationChain<I, I> =
|
||||
OperationChain()
|
||||
}
|
||||
}
|
||||
|
||||
fun <T, O> Sequence<T>.map(chain: OperationChain<T, O>): Sequence<O> =
|
||||
chain.processAll(this)
|
||||
|
||||
fun <I> chain(): OperationChain<I, I> =
|
||||
OperationChain.create()
|
||||
|
||||
fun <I, O> OperationChain<I, O>.chunked(size: Int): OperationChain<I, List<O>> =
|
||||
modifier { it.chunked(size) }
|
||||
|
||||
fun <I, O, R> OperationChain<I, O>.chunked(size: Int, transform: (List<O>) -> R): OperationChain<I, R> =
|
||||
modifier { it.chunked(size, transform) }
|
||||
|
||||
fun <I, O> OperationChain<I, O>.filter(predicate: (O) -> Boolean): OperationChain<I, O> =
|
||||
modifier { it.filter(predicate) }
|
@@ -5,6 +5,7 @@ data class SwitchCase<C, T: Any, R>(
|
||||
val then: (T) -> R,
|
||||
)
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline infix fun <C, T: Any, R> ((C)->Pair<Boolean, T?>).case(noinline then: (T) -> R) =
|
||||
SwitchCase(this, then)
|
||||
|
||||
|
@@ -1,5 +1,3 @@
|
||||
package blitz
|
||||
|
||||
typealias Provider<T> = () -> T
|
||||
|
||||
typealias Operator<I, O> = (I) -> O
|
||||
typealias Provider<T> = () -> T
|
@@ -1,7 +1,7 @@
|
||||
package blitz
|
||||
|
||||
fun unreachable(): Nothing =
|
||||
error("this should be unreachable")
|
||||
throw UnsupportedOperationException("this should be unreachable")
|
||||
|
||||
inline fun <reified R> Any?.cast(): R? =
|
||||
this?.let { if (it is R) it else null }
|
||||
this?.let { it as? R }
|
4
src/main/kotlin/blitz/annotations/Access.kt
Normal file
4
src/main/kotlin/blitz/annotations/Access.kt
Normal file
@@ -0,0 +1,4 @@
|
||||
package blitz.annotations
|
||||
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class DontAccessManually
|
25
src/main/kotlin/blitz/annotations/Mutability.kt
Normal file
25
src/main/kotlin/blitz/annotations/Mutability.kt
Normal file
@@ -0,0 +1,25 @@
|
||||
package blitz.annotations
|
||||
|
||||
/**
|
||||
* The target class is mutable.
|
||||
* Mutable methods should be annotated with [Mutate]
|
||||
*
|
||||
* @see Immutable
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class Mutable
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class Mutate
|
||||
|
||||
/**
|
||||
* The target class has no mutability that can be detected from outside.
|
||||
* It can have for example a mutable cache, but it should not affect the values of future reads.
|
||||
*
|
||||
* @see Mutable
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class Immutable
|
@@ -135,7 +135,7 @@ class ByteVec(private val initCap: Int = 0): Vec<Byte>, ByteBatchSequence {
|
||||
array[index] = value
|
||||
}
|
||||
|
||||
override fun idx(value: Byte): Int =
|
||||
override fun indexOf(value: Byte): Int =
|
||||
array.indexOf(value)
|
||||
|
||||
companion object {
|
||||
|
@@ -135,7 +135,7 @@ class CharVec(private val initCap: Int = 0): Vec<Char>, BatchSequence<Char> {
|
||||
array[index] = value
|
||||
}
|
||||
|
||||
override fun idx(value: Char): Int =
|
||||
override fun indexOf(value: Char): Int =
|
||||
array.indexOf(value)
|
||||
|
||||
companion object {
|
||||
|
@@ -3,6 +3,8 @@ package blitz.collections
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
// TODO: rewrite to math package
|
||||
|
||||
@JvmInline
|
||||
@OptIn(ExperimentalUnsignedTypes::class)
|
||||
value class Dense16x16BoolMap(
|
||||
|
@@ -132,7 +132,7 @@ class IntVec(private val initCap: Int = 0): Vec<Int>, BatchSequence<Int> {
|
||||
array[index] = value
|
||||
}
|
||||
|
||||
override fun idx(value: Int): Int =
|
||||
override fun indexOf(value: Int): Int =
|
||||
array.indexOf(value)
|
||||
|
||||
companion object {
|
||||
|
3
src/main/kotlin/blitz/collections/Iter.kt
Normal file
3
src/main/kotlin/blitz/collections/Iter.kt
Normal file
@@ -0,0 +1,3 @@
|
||||
package blitz.collections
|
||||
|
||||
typealias Iter<T> = ((T) -> Unit) -> Unit
|
@@ -33,6 +33,7 @@ fun <T> generateIterator(fn: () -> T?): Iterator<T> =
|
||||
/**
|
||||
* Can't explain this function. Look at the source of [blitz.parse.tokenize] as an example
|
||||
*/
|
||||
@Deprecated("tf is this")
|
||||
fun <T, R> Iterator<T>.funnyMap(fn: (UnGettableIterator<T>) -> R?): Iterator<R> {
|
||||
val iter = asUnGettable()
|
||||
return generateIterator { fn(iter) }
|
||||
@@ -50,4 +51,17 @@ fun <T> Iterator<T>.collect(to: Vec<T>): Vec<T> {
|
||||
to.pushBack(it)
|
||||
}
|
||||
return to
|
||||
}
|
||||
|
||||
inline fun <T> Iterator<T>.collect(crossinline consumer: (T) -> Unit) {
|
||||
forEachRemaining {
|
||||
consumer(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> Iter<T>.collect(to: Vec<T>): Vec<T> {
|
||||
this {
|
||||
to.pushBack(it)
|
||||
}
|
||||
return to
|
||||
}
|
@@ -132,7 +132,7 @@ class LongVec(private val initCap: Int = 0): Vec<Long>, BatchSequence<Long> {
|
||||
array[index] = value
|
||||
}
|
||||
|
||||
override fun idx(value: Long): Int =
|
||||
override fun indexOf(value: Long): Int =
|
||||
array.indexOf(value)
|
||||
|
||||
companion object {
|
||||
|
@@ -1,8 +1,12 @@
|
||||
package blitz.collections
|
||||
|
||||
import blitz.annotations.Mutable
|
||||
import blitz.str.MutMultiLineString
|
||||
import kotlin.math.min
|
||||
|
||||
// TODO: rewrite to Array2o
|
||||
|
||||
@Mutable
|
||||
class Matrix<T>(
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
package blitz.collections
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
|
||||
class RefVec<T>(private val initCap: Int = 0): Vec<T> {
|
||||
override var size = 0
|
||||
@JvmField var _cap = initCap
|
||||
@@ -23,7 +23,7 @@ class RefVec<T>(private val initCap: Int = 0): Vec<T> {
|
||||
inline fun copyIntoArray(arr: Array<Any?>, destOff: Int = 0, startOff: Int = 0) =
|
||||
_array?.copyInto(arr, destOff, startOff, size)
|
||||
|
||||
override inline fun copy(): RefVec<T> =
|
||||
override fun copy(): RefVec<T> =
|
||||
RefVec<T>(size).also {
|
||||
it._array?.let { copyIntoArray(it) }
|
||||
}
|
||||
@@ -101,7 +101,7 @@ class RefVec<T>(private val initCap: Int = 0): Vec<T> {
|
||||
inline fun <R> map(fn: (T) -> R): MutableList<R> =
|
||||
MutableList(size) { fn(this[it]) }
|
||||
|
||||
override fun idx(value: T): Int =
|
||||
override fun indexOf(value: T): Int =
|
||||
_array?.indexOf(value) ?: -1
|
||||
|
||||
companion object {
|
||||
|
@@ -132,7 +132,7 @@ class ShortVec(private val initCap: Int = 0): Vec<Short>, BatchSequence<Short> {
|
||||
array[index] = value
|
||||
}
|
||||
|
||||
override fun idx(value: Short): Int =
|
||||
override fun indexOf(value: Short): Int =
|
||||
array.indexOf(value)
|
||||
|
||||
companion object {
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package blitz.collections
|
||||
|
||||
@Deprecated("Results in unreadable code. Use custom tree instead")
|
||||
class Tree<T> {
|
||||
var root: Node<T>? = null
|
||||
|
||||
@@ -27,6 +28,7 @@ class Tree<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("Results in unreadable code. Use custom tree instead")
|
||||
class Node<T>(
|
||||
var value: T? = null,
|
||||
var children: MutableList<Node<T>> = mutableListOf()
|
||||
@@ -74,6 +76,7 @@ class Node<T>(
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("Results in unreadable code. Use custom tree instead")
|
||||
interface TreeTraverser<T> {
|
||||
fun process(node: Node<T>): Boolean /* true if continue, false if stop */
|
||||
|
||||
|
@@ -38,7 +38,7 @@ interface Vec<T>: IndexableSequence<T> {
|
||||
|
||||
fun clear()
|
||||
|
||||
fun idx(value: T): Int {
|
||||
fun indexOf(value: T): Int {
|
||||
for (i in 0 until size) {
|
||||
if (this[i] == value)
|
||||
return i
|
||||
|
57
src/main/kotlin/blitz/math/Aabb3f.kt
Normal file
57
src/main/kotlin/blitz/math/Aabb3f.kt
Normal file
@@ -0,0 +1,57 @@
|
||||
package blitz.math
|
||||
|
||||
data class Aabb3f(
|
||||
@JvmField val min: Vec3f,
|
||||
@JvmField val max: Vec3f,
|
||||
) {
|
||||
inline val height get() = max.y - min.y
|
||||
inline val width get() = max.x - min.x
|
||||
inline val depth get() = max.z - min.z
|
||||
|
||||
companion object {
|
||||
fun from(other: Aabb3s) =
|
||||
Aabb3f(
|
||||
Vec3f(other.min.x.toFloat(), other.min.y.toFloat(), other.min.z.toFloat()),
|
||||
Vec3f(other.max.x.toFloat(), other.max.y.toFloat(), other.max.z.toFloat())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A vector representing the size of the box. (width, height, depth)
|
||||
*/
|
||||
val size: Vec3f get() =
|
||||
Vec3f(width, height, depth)
|
||||
|
||||
operator fun contains(point: Vec3f) =
|
||||
point.x >= min.x && point.x <= max.x &&
|
||||
point.y >= min.y && point.y <= max.y &&
|
||||
point.z >= min.z && point.z <= max.z
|
||||
|
||||
operator fun rangeTo(other: Aabb3f): Aabb3f =
|
||||
Aabb3f(
|
||||
min = Vec3f(
|
||||
minOf(min.x, other.min.x),
|
||||
minOf(min.y, other.min.y),
|
||||
minOf(min.z, other.min.z)
|
||||
),
|
||||
max = Vec3f(
|
||||
maxOf(max.x, other.max.x),
|
||||
maxOf(max.y, other.max.y),
|
||||
maxOf(max.z, other.max.z)
|
||||
)
|
||||
)
|
||||
|
||||
operator fun rangeTo(other: Vec3f) =
|
||||
Aabb3f(
|
||||
min = Vec3f(
|
||||
minOf(min.x, other.x),
|
||||
minOf(min.y, other.y),
|
||||
minOf(min.z, other.z)
|
||||
),
|
||||
max = Vec3f(
|
||||
maxOf(max.x, other.x),
|
||||
maxOf(max.y, other.y),
|
||||
maxOf(max.z, other.z)
|
||||
)
|
||||
)
|
||||
}
|
90
src/main/kotlin/blitz/math/Aabb3s.kt
Normal file
90
src/main/kotlin/blitz/math/Aabb3s.kt
Normal file
@@ -0,0 +1,90 @@
|
||||
package blitz.math
|
||||
|
||||
import blitz.Endian
|
||||
import blitz.annotations.Immutable
|
||||
import blitz.toBytes
|
||||
import blitz.toLong
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
|
||||
@Immutable
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
class Aabb3s private constructor(
|
||||
val min: Vec3s,
|
||||
val max: Vec3s
|
||||
) {
|
||||
inline fun copy(min: Vec3s = this.min, max: Vec3s = this.max) =
|
||||
of(min, max)
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is Aabb3s && other.min == min && other.max == max
|
||||
|
||||
override fun hashCode(): Int =
|
||||
31 * min.hashCode() + max.hashCode()
|
||||
|
||||
override fun toString(): String =
|
||||
"($min, $max)"
|
||||
|
||||
operator fun contains(point: Vec3s): Boolean =
|
||||
point.x in min.x..max.x && point.y in min.y..max.y && point.z in min.z..max.z
|
||||
|
||||
operator fun contains(other: Aabb3s): Boolean =
|
||||
contains(other.min) && contains(other.max)
|
||||
|
||||
operator fun rangeTo(include: Vec3s): Aabb3s {
|
||||
val actualMin = Vec3s(minOf(min.x, include.x), minOf(min.y, include.y), minOf(min.z, include.z))
|
||||
val actualMax = Vec3s(maxOf(max.x, include.x), maxOf(max.y, include.y), maxOf(max.z, include.z))
|
||||
return Aabb3s(actualMin, actualMax)
|
||||
}
|
||||
|
||||
operator fun rangeTo(include: Aabb3s): Aabb3s {
|
||||
val actualMin = Vec3s(minOf(min.x, include.min.x), minOf(min.y, include.min.y), minOf(min.z, include.min.z))
|
||||
val actualMax = Vec3s(maxOf(max.x, include.max.x), maxOf(max.y, include.max.y), maxOf(max.z, include.max.z))
|
||||
return Aabb3s(actualMin, actualMax)
|
||||
}
|
||||
|
||||
operator fun plus(off: Vec3s) =
|
||||
Aabb3s(min + off, max + off)
|
||||
|
||||
operator fun minus(off: Vec3s) =
|
||||
Aabb3s(min - off, max - off)
|
||||
|
||||
inline fun iter(consumer: (Vec3s) -> Unit) {
|
||||
for (y in min.y..max.y) {
|
||||
for (x in min.x..max.x) {
|
||||
for (z in min.z..max.z) {
|
||||
consumer(Vec3s(x, y, z))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun serialize(put: (ByteArray) -> Unit) {
|
||||
contract {
|
||||
callsInPlace(put, InvocationKind.AT_LEAST_ONCE)
|
||||
}
|
||||
|
||||
min.packed.toBytes(Endian.Little).let(put)
|
||||
max.packed.toBytes(Endian.Little).let(put)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun of(a: Vec3s, b: Vec3s): Aabb3s =
|
||||
Aabb3s(a, a)..b
|
||||
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
inline fun deserialize(get: (Int) -> ByteArray): Aabb3s {
|
||||
contract {
|
||||
callsInPlace(get, InvocationKind.AT_LEAST_ONCE)
|
||||
}
|
||||
|
||||
val min = Vec3s.from(get(8).toLong(Endian.LITTLE).toULong())
|
||||
val max = Vec3s.from(get(8).toLong(Endian.LITTLE).toULong())
|
||||
return of(min, max)
|
||||
}
|
||||
}
|
||||
}
|
51
src/main/kotlin/blitz/math/Anglef.kt
Normal file
51
src/main/kotlin/blitz/math/Anglef.kt
Normal file
@@ -0,0 +1,51 @@
|
||||
package blitz.math
|
||||
|
||||
@JvmInline
|
||||
value class Anglef(
|
||||
val radians: Float
|
||||
) {
|
||||
val degrees get() =
|
||||
radians * 180f / Math.PI.toFloat()
|
||||
|
||||
val gradians get() =
|
||||
radians * 200f / Math.PI.toFloat()
|
||||
|
||||
fun normalized() =
|
||||
Anglef(radians % (2 * Math.PI.toFloat()))
|
||||
|
||||
operator fun plus(other: Anglef) =
|
||||
Anglef(radians + other.radians)
|
||||
|
||||
operator fun minus(other: Anglef) =
|
||||
Anglef(radians - other.radians)
|
||||
|
||||
operator fun times(other: Anglef) =
|
||||
Anglef(radians * other.radians)
|
||||
|
||||
operator fun div(other: Anglef) =
|
||||
Anglef(radians / other.radians)
|
||||
|
||||
operator fun times(other: Float) =
|
||||
Anglef(radians * other)
|
||||
|
||||
operator fun div(other: Float) =
|
||||
Anglef(radians / other)
|
||||
|
||||
operator fun unaryMinus() =
|
||||
Anglef(-radians)
|
||||
|
||||
override fun toString(): String {
|
||||
return "$degrees°"
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromRadians(radians: Float) =
|
||||
Anglef(radians)
|
||||
|
||||
fun fromDegrees(degrees: Float) =
|
||||
Anglef(degrees * Math.PI.toFloat() / 180f)
|
||||
|
||||
fun fromGradians(gradians: Float) =
|
||||
Anglef(gradians * Math.PI.toFloat() / 200f)
|
||||
}
|
||||
}
|
41
src/main/kotlin/blitz/math/BitInts.kt
Normal file
41
src/main/kotlin/blitz/math/BitInts.kt
Normal file
@@ -0,0 +1,41 @@
|
||||
package blitz.math
|
||||
|
||||
import blitz.annotations.Immutable
|
||||
|
||||
@Immutable
|
||||
@JvmInline
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
value class U3(val value: UByte) {
|
||||
override fun toString() =
|
||||
value.toString()
|
||||
|
||||
inline fun toU4() = U4(value)
|
||||
}
|
||||
|
||||
@Immutable
|
||||
@JvmInline
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
value class U4(val value: UByte) {
|
||||
override fun toString() =
|
||||
value.toString()
|
||||
|
||||
inline fun toU3() = U3(value and 0b111u)
|
||||
}
|
||||
|
||||
|
||||
/** two U4 values packed => UByte */
|
||||
@Immutable
|
||||
@JvmInline
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
value class Vec2u4(val packed: UByte) {
|
||||
inline val x : U4 get() = U4((packed.toUInt() shr 4).toUByte())
|
||||
inline val y : U4 get() = U4(packed and 0b111u)
|
||||
|
||||
constructor(x: U4, y: U4): this((x.value.toUInt() shl 4 or
|
||||
y.value.toUInt()).toUByte())
|
||||
|
||||
override fun toString() = "($x, $y)"
|
||||
|
||||
inline fun component1() = x
|
||||
inline fun component2() = y
|
||||
}
|
29
src/main/kotlin/blitz/math/Direction.kt
Normal file
29
src/main/kotlin/blitz/math/Direction.kt
Normal file
@@ -0,0 +1,29 @@
|
||||
package blitz.math
|
||||
|
||||
import blitz.unreachable
|
||||
|
||||
/** only 6 directions -> only needs 3 bits */
|
||||
@JvmInline
|
||||
value class Direction(
|
||||
val idx: U3
|
||||
) {
|
||||
companion object {
|
||||
val UP = Direction(U3(0u))
|
||||
val DOWN = Direction(U3(1u))
|
||||
val NORTH = Direction(U3(2u))
|
||||
val EAST = Direction(U3(3u))
|
||||
val SOUTH = Direction(U3(4u))
|
||||
val WEST = Direction(U3(5u))
|
||||
}
|
||||
|
||||
override fun toString() =
|
||||
when (this) {
|
||||
UP -> "Up"
|
||||
DOWN -> "Down"
|
||||
NORTH -> "North"
|
||||
EAST -> "East"
|
||||
SOUTH -> "South"
|
||||
WEST -> "West"
|
||||
else -> unreachable()
|
||||
}
|
||||
}
|
@@ -3,8 +3,14 @@ package blitz.math
|
||||
infix fun Pair<Int, Int>.lerpi(t: Double): Double =
|
||||
(1.0f - t) * first + second * t
|
||||
|
||||
infix fun Pair<Int, Int>.lerpi(t: Float): Float =
|
||||
(1.0f - t) * first + second * t
|
||||
|
||||
infix fun Pair<Double, Double>.lerpd(t: Double): Double =
|
||||
(1.0f - t) * first + second * t
|
||||
|
||||
infix fun Pair<Float, Float>.lerpf(t: Double): Double =
|
||||
(1.0f - t) * first + second * t
|
||||
|
||||
infix fun Pair<Float, Float>.lerpf(t: Float): Float =
|
||||
(1.0f - t) * first + second * t
|
146
src/main/kotlin/blitz/math/PackedVoxelShape3i.kt
Normal file
146
src/main/kotlin/blitz/math/PackedVoxelShape3i.kt
Normal file
@@ -0,0 +1,146 @@
|
||||
package blitz.math
|
||||
|
||||
import blitz.annotations.Mutable
|
||||
import blitz.annotations.Mutate
|
||||
import blitz.test.annotations.Test
|
||||
import blitz.test.util.requireEqual
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
/**
|
||||
* use this only for small-ish voxel shapes: 16x16, 32x16, etc
|
||||
*
|
||||
* size needs to be known at construction
|
||||
*/
|
||||
@Mutable
|
||||
@JvmInline
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
value class PackedVoxelShape3i(
|
||||
val packed: LongArray,
|
||||
) {
|
||||
inline val sizes4x4x4 get() =
|
||||
Vec3s.from(packed[0].toULong())
|
||||
|
||||
val sizes get() =
|
||||
Vec3s(sizes4x4x4.x * 4.toShort(), sizes4x4x4.y * 4.toShort(), sizes4x4x4.z * 4.toShort())
|
||||
|
||||
inline fun index4x4x4(x: Int, y: Int, z: Int) =
|
||||
1 + x + sizes4x4x4.x.toInt() * (y + sizes4x4x4.y.toInt() * z)
|
||||
|
||||
operator fun get(x: Int, y: Int, z: Int): Boolean {
|
||||
require(Vec3s(x, y, z).allComponents(sizes) { a, sz -> a < sz })
|
||||
return VoxelShape4x4x4i(packed[index4x4x4(x / 4, y / 4, z / 4)])[x % 4, y % 4, z % 4]
|
||||
}
|
||||
|
||||
inline operator fun get(idx: Vec3s) =
|
||||
get(idx.x.toInt(), idx.y.toInt(), idx.z.toInt())
|
||||
|
||||
@Mutate
|
||||
operator fun set(x: Int, y: Int, z: Int, value: Boolean) {
|
||||
require(Vec3s(x, y, z).allComponents(sizes) { a, sz -> a < sz }) { "index out of bounds: ($x, $y, $z) for $sizes" }
|
||||
val idx = index4x4x4(x / 4, y / 4, z / 4)
|
||||
packed[idx] = VoxelShape4x4x4i(packed[idx]).set(x % 4, y % 4, z % 4, value).packed
|
||||
}
|
||||
|
||||
@Mutate
|
||||
inline operator fun set(idx: Vec3s, value: Boolean) =
|
||||
set(idx.x.toInt(), idx.y.toInt(), idx.z.toInt(), value)
|
||||
|
||||
inline fun iterChunks(consumer: (Vec3s, VoxelShape4x4x4i) -> Unit) {
|
||||
contract {
|
||||
callsInPlace(consumer)
|
||||
}
|
||||
|
||||
sizes4x4x4.asSizeIterate {
|
||||
consumer(it,
|
||||
VoxelShape4x4x4i(
|
||||
packed[index4x4x4(it.x.toInt(), it.y.toInt(), it.z.toInt())]))
|
||||
}
|
||||
}
|
||||
|
||||
inline fun iterSetVoxels(consumer: (Vec3s) -> Unit) {
|
||||
contract {
|
||||
callsInPlace(consumer)
|
||||
}
|
||||
|
||||
iterChunks { cp, chunk ->
|
||||
chunk.iterSetVoxels { off ->
|
||||
consumer(cp * 4 + off)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun layerEmpty(y: Int): Boolean {
|
||||
val cy = y / 4
|
||||
for (cx in 0..<sizes4x4x4.x) {
|
||||
for (cz in 0..<sizes4x4x4.z) {
|
||||
val chunk = VoxelShape4x4x4i(packed[index4x4x4(cx, cy, cz)])
|
||||
if (chunk.empty) continue
|
||||
val layer = chunk[y % 4]
|
||||
if (!layer.empty) return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
inline fun iterNonEmptyLayers(consumer: (Short) -> Unit) {
|
||||
contract {
|
||||
callsInPlace(consumer)
|
||||
}
|
||||
|
||||
for (y in 0..<sizes4x4x4.y * 4) {
|
||||
if (!layerEmpty(y)) {
|
||||
consumer(y.toShort())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun copy() = PackedVoxelShape3i(packed.copyOf())
|
||||
|
||||
override fun toString(): String {
|
||||
val out = StringBuilder()
|
||||
iterNonEmptyLayers { y ->
|
||||
out.append("layer $y:\n")
|
||||
for (x in 0..<sizes.x) {
|
||||
out.append(" ")
|
||||
for (z in 0..<sizes.z) {
|
||||
out.append(if (this[x, y.toInt(), z]) '#' else '.')
|
||||
}
|
||||
out.append('\n')
|
||||
}
|
||||
}
|
||||
return out.toString()
|
||||
}
|
||||
|
||||
fun aabb(): Aabb3s? {
|
||||
var out: Aabb3s? = null
|
||||
iterChunks { cp, chunk ->
|
||||
chunk.aabb()?.let {
|
||||
val x = it + cp * 4
|
||||
out = out?.let { it..x } ?: x
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun of(size: Vec3s): PackedVoxelShape3i {
|
||||
val sizes4x4x4 = size.ceilDiv(4)
|
||||
val obj = PackedVoxelShape3i(LongArray(sizes4x4x4.x.toInt() * sizes4x4x4.y.toInt() * sizes4x4x4.z.toInt() + 1))
|
||||
obj.packed[0] = sizes4x4x4.packed.toLong()
|
||||
return obj
|
||||
}
|
||||
}
|
||||
|
||||
object _tests {
|
||||
@Test
|
||||
fun aabbCorrect() {
|
||||
val voxels = of(Vec3s(8, 6, 8))
|
||||
voxels[3,2,1] = true
|
||||
voxels[6,2,2] = true
|
||||
voxels[7,4,2] = true
|
||||
requireEqual(voxels.aabb(), Aabb3s.of(Vec3s(3, 2, 1), Vec3s(7, 4, 2)))
|
||||
}
|
||||
}
|
||||
}
|
98
src/main/kotlin/blitz/math/Vec3f.kt
Normal file
98
src/main/kotlin/blitz/math/Vec3f.kt
Normal file
@@ -0,0 +1,98 @@
|
||||
package blitz.math
|
||||
|
||||
import blitz.Endian
|
||||
import blitz.annotations.Immutable
|
||||
import blitz.toBytes
|
||||
import blitz.toInt
|
||||
import blitz.unreachable
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
|
||||
@Immutable
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
data class Vec3f(
|
||||
@JvmField val x: Float,
|
||||
@JvmField val y: Float,
|
||||
@JvmField val z: Float
|
||||
) {
|
||||
|
||||
inline fun serialize(put: (ByteArray) -> Unit) {
|
||||
contract {
|
||||
callsInPlace(put, InvocationKind.AT_LEAST_ONCE)
|
||||
}
|
||||
|
||||
x.toRawBits().toBytes(Endian.Little).let(put)
|
||||
y.toRawBits().toBytes(Endian.Little).let(put)
|
||||
z.toRawBits().toBytes(Endian.Little).let(put)
|
||||
}
|
||||
|
||||
companion object {
|
||||
inline fun from(s: Vec3s) =
|
||||
Vec3f(s.x.toFloat(), s.y.toFloat(), s.z.toFloat())
|
||||
|
||||
val Zero = Vec3f(0f, 0f, 0f)
|
||||
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
inline fun deserialize(get: (Int) -> ByteArray): Vec3f {
|
||||
contract {
|
||||
callsInPlace(get, InvocationKind.AT_LEAST_ONCE)
|
||||
}
|
||||
|
||||
return Vec3f(
|
||||
get(4).toInt(Endian.Little).let(Float::fromBits),
|
||||
get(4).toInt(Endian.Little).let(Float::fromBits),
|
||||
get(4).toInt(Endian.Little).let(Float::fromBits),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString() =
|
||||
"($x, $y, $z)"
|
||||
|
||||
|
||||
fun rotate(direction: Direction): Vec3f =
|
||||
when (direction) {
|
||||
Direction.DOWN -> Vec3f(x, -z, y)
|
||||
Direction.UP -> Vec3f(x, z, -y)
|
||||
Direction.NORTH -> Vec3f(x, y, z)
|
||||
Direction.SOUTH -> Vec3f(-x, y, -z)
|
||||
Direction.WEST -> Vec3f(z, y, -x)
|
||||
Direction.EAST -> Vec3f(-z, y, x)
|
||||
else -> unreachable()
|
||||
}
|
||||
|
||||
inline operator fun plus(r: Vec3f): Vec3f =
|
||||
Vec3f(this.x + r.x, this.y + r.y, this.z + r.z)
|
||||
|
||||
inline operator fun minus(r: Vec3f): Vec3f =
|
||||
Vec3f(this.x - r.x, this.y - r.y, this.z - r.z)
|
||||
|
||||
inline operator fun times(r: Vec3f): Vec3f =
|
||||
Vec3f(this.x * r.x, this.y * r.y, this.z * r.z)
|
||||
|
||||
inline operator fun div(r: Vec3f): Vec3f =
|
||||
Vec3f(this.x / r.x, this.y / r.y, this.z / r.z)
|
||||
|
||||
inline infix fun dot(r: Vec3f) =
|
||||
this.x * r.x + this.y * r.y + this.z * r.z
|
||||
|
||||
inline fun product() = x * y * z
|
||||
|
||||
inline operator fun unaryMinus() =
|
||||
Vec3f(-x, -y, -z)
|
||||
|
||||
inline fun allComponents(fn: (Float) -> Boolean) =
|
||||
fn(x) && fn(y) && fn(z)
|
||||
|
||||
inline fun anyComponents(fn: (Float) -> Boolean) =
|
||||
fn(x) || fn(y) || fn(z)
|
||||
|
||||
inline fun allComponents(other: Vec3f, fn: (Float, Float) -> Boolean) =
|
||||
fn(x, other.x) && fn(y, other.y) && fn(z, other.z)
|
||||
|
||||
inline fun anyComponent(other: Vec3f, fn: (Float, Float) -> Boolean) =
|
||||
fn(x, other.x) || fn(y, other.y) || fn(z, other.z)
|
||||
}
|
136
src/main/kotlin/blitz/math/Vec3s.kt
Normal file
136
src/main/kotlin/blitz/math/Vec3s.kt
Normal file
@@ -0,0 +1,136 @@
|
||||
package blitz.math
|
||||
|
||||
import blitz.annotations.Immutable
|
||||
import blitz.unreachable
|
||||
import blitz.test.annotations.Test
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
@Immutable
|
||||
@JvmInline
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
value class Vec3s private constructor(val backing: Vec4s) {
|
||||
inline val x: Short get() = backing.x
|
||||
inline val y: Short get() = backing.y
|
||||
inline val z: Short get() = backing.z
|
||||
|
||||
inline val packed: ULong get() = backing.packed
|
||||
|
||||
companion object {
|
||||
fun from(xyz: Vec4s) =
|
||||
Vec3s(Vec4s(xyz.packed and 0xFFFF_FFFF_FFFF_0000uL))
|
||||
|
||||
inline fun from(packed: ULong) =
|
||||
from(Vec4s(packed))
|
||||
|
||||
inline fun from(v: Vec3f) =
|
||||
Vec3s(v.x.toInt(), v.y.toInt(), v.z.toInt())
|
||||
|
||||
@JvmStatic
|
||||
private fun Int.toShortThrow() = toShort().also {
|
||||
if (it.toInt() != this) throw IllegalArgumentException("Value $this cannot be represented as Short")
|
||||
}
|
||||
|
||||
val Zero = Vec3s(0, 0, 0)
|
||||
}
|
||||
|
||||
@Suppress("UNUSED")
|
||||
object _tests {
|
||||
@Test
|
||||
fun fromPackedCorrect() {
|
||||
require(from(Vec4s(1,2,3,4).packed) == Vec3s(1,2,3))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun serialFormatStable() {
|
||||
require(Vec4s(1,2,3,4).packed == 0x0001_0002_0003_0004uL)
|
||||
require(Vec4s(0x0001_0002_0003_0004uL) == Vec4s(1,2,3,4))
|
||||
}
|
||||
}
|
||||
|
||||
constructor(x: Short, y: Short, z: Short): this(Vec4s(x, y, z, 0))
|
||||
constructor(x: Int, y: Int, z: Int): this(Vec4s(x.toShortThrow(), y.toShortThrow(), z.toShortThrow(), 0))
|
||||
|
||||
override fun toString() = "($x, $y, $z)"
|
||||
|
||||
inline operator fun component1() = x
|
||||
inline operator fun component2() = y
|
||||
inline operator fun component3() = z
|
||||
|
||||
inline fun asSizeIterate(consumer: (Vec3s) -> Unit) {
|
||||
contract {
|
||||
callsInPlace(consumer)
|
||||
}
|
||||
|
||||
for (z in 0..<z) {
|
||||
for (x in 0..<x) {
|
||||
for (y in 0..<y) {
|
||||
consumer(Vec3s(x, y, z))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun rotate(direction: Direction): Vec3s =
|
||||
when (direction) {
|
||||
Direction.DOWN -> Vec3s(x, (-z).toShort(), y)
|
||||
Direction.UP -> Vec3s(x, z, (-y).toShort())
|
||||
Direction.NORTH -> Vec3s(x, y, z)
|
||||
Direction.SOUTH -> Vec3s((-x).toShort(), y, (-z).toShort())
|
||||
Direction.WEST -> Vec3s(z, y, (-x).toShort())
|
||||
Direction.EAST -> Vec3s((-z).toShort(), y, x)
|
||||
else -> unreachable()
|
||||
}
|
||||
|
||||
inline operator fun plus(r: Vec3s): Vec3s =
|
||||
Vec3s(this.x + r.x, this.y + r.y, this.z + r.z)
|
||||
|
||||
inline operator fun minus(r: Vec3s): Vec3s =
|
||||
Vec3s(this.x - r.x, this.y - r.y, this.z - r.z)
|
||||
|
||||
inline operator fun times(r: Vec3s): Vec3s =
|
||||
Vec3s(this.x * r.x, this.y * r.y, this.z * r.z)
|
||||
|
||||
inline operator fun plus(off: Short): Vec3s =
|
||||
Vec3s(this.x + off, this.y + off, this.z + off)
|
||||
|
||||
inline operator fun minus(off: Short): Vec3s =
|
||||
Vec3s(this.x - off, this.y - off, this.z - off)
|
||||
|
||||
operator fun times(value: Float): Vec3s =
|
||||
Vec3s((x * value).toInt().toShortThrow(), (y * value).toInt().toShortThrow(), (z * value).toInt().toShortThrow())
|
||||
|
||||
operator fun times(value: Short): Vec3s =
|
||||
Vec3s(x * value, y * value, z * value)
|
||||
|
||||
operator fun div(value: Float): Vec3s =
|
||||
Vec3s((x / value).toInt().toShortThrow(), (y / value).toInt().toShortThrow(), (z / value).toInt().toShortThrow())
|
||||
|
||||
operator fun div(value: Short): Vec3s =
|
||||
Vec3s(x / value, y / value, z / value)
|
||||
|
||||
fun ceilDiv(value: Short): Vec3s =
|
||||
Vec3s(
|
||||
if (x >= 0) (x / value) + 1 else (x / value),
|
||||
if (y >= 0) (y / value) + 1 else (y / value),
|
||||
if (z >= 0) (z / value) + 1 else (z / value)
|
||||
)
|
||||
|
||||
inline fun product() = x.toInt() * y.toInt() * z.toInt()
|
||||
|
||||
inline operator fun unaryMinus() =
|
||||
Vec3s(-x, -y, -z)
|
||||
|
||||
inline fun allComponents(fn: (Short) -> Boolean) =
|
||||
fn(x) && fn(y) && fn(z)
|
||||
|
||||
inline fun anyComponents(fn: (Short) -> Boolean) =
|
||||
fn(x) || fn(y) || fn(z)
|
||||
|
||||
inline fun allComponents(other: Vec3s, fn: (Short, Short) -> Boolean) =
|
||||
fn(x, other.x) && fn(y, other.y) && fn(z, other.z)
|
||||
|
||||
inline fun anyComponent(other: Vec3s, fn: (Short, Short) -> Boolean) =
|
||||
fn(x, other.x) || fn(y, other.y) || fn(z, other.z)
|
||||
}
|
26
src/main/kotlin/blitz/math/Vec4s.kt
Normal file
26
src/main/kotlin/blitz/math/Vec4s.kt
Normal file
@@ -0,0 +1,26 @@
|
||||
package blitz.math
|
||||
|
||||
import blitz.annotations.Immutable
|
||||
|
||||
@Immutable
|
||||
@JvmInline
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
value class Vec4s(val packed: ULong) {
|
||||
inline val x: Short get() = (packed shr 48).toUShort().toShort()
|
||||
inline val y: Short get() = (packed shr 32).toUShort().toShort()
|
||||
inline val z: Short get() = (packed shr 16).toUShort().toShort()
|
||||
inline val w: Short get() = packed.toUShort().toShort()
|
||||
|
||||
constructor(x: Short, y: Short, z: Short, w: Short): this(
|
||||
(x.toUShort().toULong() shl 48) or
|
||||
(y.toUShort().toULong() shl 32) or
|
||||
(z.toUShort().toULong() shl 16) or
|
||||
w.toUShort().toULong())
|
||||
|
||||
override fun toString() = "($x, $y, $z, $w)"
|
||||
|
||||
inline operator fun component1() = x
|
||||
inline operator fun component2() = y
|
||||
inline operator fun component3() = z
|
||||
inline operator fun component4() = w
|
||||
}
|
40
src/main/kotlin/blitz/math/VoxelShape4x4i.kt
Normal file
40
src/main/kotlin/blitz/math/VoxelShape4x4i.kt
Normal file
@@ -0,0 +1,40 @@
|
||||
package blitz.math
|
||||
|
||||
import blitz.annotations.Immutable
|
||||
import kotlin.experimental.and
|
||||
import kotlin.experimental.or
|
||||
|
||||
@Immutable
|
||||
@JvmInline
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
value class VoxelShape4x4i(
|
||||
val packed: Short,
|
||||
) {
|
||||
fun bitIndexFor(x: Int, y: Int): Int {
|
||||
require(x in 0..3 && y in 0..3) { "index out of bounds: ($x, $y) for 4x4" }
|
||||
return 4 * y + x
|
||||
}
|
||||
|
||||
inline operator fun get(x: Int, y: Int): Boolean =
|
||||
(packed.toUShort().toInt() and (1 shl bitIndexFor(x, y))) != 0
|
||||
|
||||
inline fun set(x: Int, y: Int, value: Boolean): VoxelShape4x4i {
|
||||
val mask = 1 shl bitIndexFor(x, y)
|
||||
return VoxelShape4x4i((if (value)
|
||||
packed.toUShort().toInt() or mask
|
||||
else
|
||||
packed.toUShort().toInt() and mask.inv()).toShort())
|
||||
}
|
||||
|
||||
inline operator fun plus(other: VoxelShape4x4i) =
|
||||
VoxelShape4x4i(packed or other.packed)
|
||||
|
||||
inline operator fun times(other: VoxelShape4x4i) =
|
||||
VoxelShape4x4i(packed and other.packed)
|
||||
|
||||
inline val empty get() = packed.toInt() == 0
|
||||
|
||||
companion object {
|
||||
val Air = VoxelShape4x4i(0)
|
||||
}
|
||||
}
|
93
src/main/kotlin/blitz/math/VoxelShape4x4x4i.kt
Normal file
93
src/main/kotlin/blitz/math/VoxelShape4x4x4i.kt
Normal file
@@ -0,0 +1,93 @@
|
||||
package blitz.math
|
||||
|
||||
import blitz.annotations.Immutable
|
||||
import blitz.test.annotations.Test
|
||||
import blitz.test.util.requireEqual
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
@Immutable
|
||||
@JvmInline
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
value class VoxelShape4x4x4i(
|
||||
val packed: Long
|
||||
) {
|
||||
operator fun get(y: Int): VoxelShape4x4i {
|
||||
require(y in 0..3) { "layer index out of bounds: y=$y (expected 0..3)" }
|
||||
val shift = 16 * y
|
||||
return VoxelShape4x4i(((packed ushr shift) and 0xFFFFL).toShort())
|
||||
}
|
||||
|
||||
fun set(y: Int, value: VoxelShape4x4i): VoxelShape4x4x4i {
|
||||
require(y in 0..3) { "layer index out of bounds: y=$y (expected 0..3)" }
|
||||
val shift = 16 * y
|
||||
val clearMask = (0xFFFFL shl shift).inv()
|
||||
val slice = ((value.packed.toUShort().toLong() and 0xFFFFL) shl shift)
|
||||
return VoxelShape4x4x4i((packed and clearMask) or slice)
|
||||
}
|
||||
|
||||
inline operator fun plus(other: VoxelShape4x4x4i) =
|
||||
VoxelShape4x4x4i(packed or other.packed)
|
||||
|
||||
inline operator fun times(other: VoxelShape4x4x4i) =
|
||||
VoxelShape4x4x4i(packed and other.packed)
|
||||
|
||||
inline operator fun get(x: Int, y: Int, z: Int): Boolean =
|
||||
this[y][x, z]
|
||||
|
||||
inline fun set(x: Int, y: Int, z: Int, value: Boolean) =
|
||||
set(y, this[y].set(x, z, value))
|
||||
|
||||
inline val empty get() = packed == 0L
|
||||
|
||||
inline fun iterSetVoxels(consumer: (Vec3s) -> Unit) {
|
||||
contract {
|
||||
callsInPlace(consumer)
|
||||
}
|
||||
|
||||
if (empty) return
|
||||
|
||||
for (y in 0..<4) {
|
||||
for (x in 0..<4) {
|
||||
for (z in 0..<4) {
|
||||
if (this[x, y, z]) {
|
||||
consumer(Vec3s(x, y, z))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun aabb(): Aabb3s? {
|
||||
if (empty) return null
|
||||
val minBit = packed.countTrailingZeroBits()
|
||||
val maxBit = 63 - packed.countLeadingZeroBits()
|
||||
return Aabb3s.of(
|
||||
bitToPos(minBit),
|
||||
bitToPos(maxBit)
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val AIR = VoxelShape4x4x4i(0)
|
||||
|
||||
private fun bitToPos(bit: Int): Vec3s {
|
||||
// bit = x + 4 * z + 16 * y
|
||||
val pos2 = bit % 16
|
||||
val x = pos2 % 4
|
||||
val z = pos2 / 4
|
||||
return Vec3s(x.toShort(), (bit / 16).toShort(), z.toShort())
|
||||
}
|
||||
}
|
||||
|
||||
object _test {
|
||||
@Test
|
||||
fun aabbCorrect() {
|
||||
val x = AIR
|
||||
.set(2, 3, 1, true)
|
||||
.set(3, 3, 1, true)
|
||||
requireEqual(x.aabb(), Aabb3s.of(Vec3s(2, 3, 1), Vec3s(3, 3, 1)))
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,10 +3,12 @@ package blitz.parse
|
||||
import blitz.collections.RefVec
|
||||
import blitz.collections.contents
|
||||
import blitz.parse.comb2.*
|
||||
import blitz.test.annotations.Test
|
||||
import blitz.test.util.requireEqual
|
||||
import blitz.unreachable
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
object JSON {
|
||||
|
||||
val jsonBool: Parser<Char, Element> = choose {
|
||||
it(mapValue(seq("true".toList())) { Element.newBool(true) })
|
||||
it(mapValue(seq("false".toList())) { Element.newBool(false) })
|
||||
@@ -116,31 +118,72 @@ object JSON {
|
||||
inline fun Element.uncheckedAsObj(): Map<String, Element> =
|
||||
_boxed as Map<String, Element>
|
||||
|
||||
inline fun Element.asNum(): Double {
|
||||
fun Element.asNum(): Double {
|
||||
require(kind == Element.NUM) { "Element is not a Number" }
|
||||
return _num
|
||||
}
|
||||
|
||||
inline fun Element.asBool(): Boolean {
|
||||
fun Element.asBool(): Boolean {
|
||||
require(kind == Element.BOOL) { "Element is not a Boolean" }
|
||||
return _bool
|
||||
}
|
||||
|
||||
inline fun Element.asArr(): RefVec<Element> {
|
||||
fun Element.asArr(): RefVec<Element> {
|
||||
require(kind == Element.ARR) { "Element is not an Array" }
|
||||
return _boxed as RefVec<Element>
|
||||
}
|
||||
|
||||
inline fun Element.asStr(): String {
|
||||
fun Element.asStr(): String {
|
||||
require(kind == Element.STR) { "Element is not a String" }
|
||||
return _boxed as String
|
||||
}
|
||||
|
||||
inline fun Element.asObj(): Map<String, Element> {
|
||||
fun Element.asObj(): Map<String, Element> {
|
||||
require(kind == Element.OBJ) { "Element is not an Object" }
|
||||
return _boxed as Map<String, Element>
|
||||
}
|
||||
|
||||
fun parse(string: String): ParseResult<Element> =
|
||||
jsonElement.run(string.toList())
|
||||
|
||||
object _tests {
|
||||
@Test
|
||||
fun parseJsonNumber() {
|
||||
parse("-1.351").assertA().asNum()
|
||||
.requireEqual(-1.351)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseJsonBool() {
|
||||
parse("true").assertA().asBool()
|
||||
.requireEqual(true)
|
||||
parse("false").assertA().asBool()
|
||||
.requireEqual(false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseJsonNull() {
|
||||
parse("null").assertA().kind
|
||||
.requireEqual(Element.NULL)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseJsonStr() {
|
||||
parse("\"Hello\\\n\\\"aworld\"").assertA().asStr()
|
||||
.requireEqual("Hello\n\"aworld")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseJsonArr() {
|
||||
parse("[1, 2, 3,\n 4]").assertA().asArr()
|
||||
.map { it.asNum() }.contents.requireEqual(listOf(1.0,2.0,3.0,4.0).contents)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseJsonObj() {
|
||||
val obj = parse("{\"a\": 1, \"b\": 2}").assertA().asObj()
|
||||
obj.map { it.value.asNum() }.contents.requireEqual(listOf(1.0,2.0).contents)
|
||||
obj.map { it.key }.contents.requireEqual(listOf("a","b").contents)
|
||||
}
|
||||
}
|
||||
}
|
@@ -77,17 +77,6 @@ object Terminal {
|
||||
System.err.println(encodeString(str, *modes))
|
||||
}
|
||||
|
||||
@Deprecated(
|
||||
"Use errln instead!",
|
||||
ReplaceWith(
|
||||
"errln(str, *modes)",
|
||||
"blitz.term.Terminal.errln"
|
||||
)
|
||||
)
|
||||
fun warn(str: String, vararg modes: AnsiiMode) {
|
||||
errln(str, *modes)
|
||||
}
|
||||
|
||||
object Cursor {
|
||||
fun savePos() {
|
||||
print(escape + "7")
|
||||
|
5
src/main/kotlin/blitz/test/annotations/Test.kt
Normal file
5
src/main/kotlin/blitz/test/annotations/Test.kt
Normal file
@@ -0,0 +1,5 @@
|
||||
package blitz.test.annotations
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class Test
|
7
src/main/kotlin/blitz/test/util/Asserts.kt
Normal file
7
src/main/kotlin/blitz/test/util/Asserts.kt
Normal file
@@ -0,0 +1,7 @@
|
||||
package blitz.test.util
|
||||
|
||||
fun requireEqual(a: Any?, b: Any?) =
|
||||
require(a == b) { "Expected $a to be equal to $b"}
|
||||
|
||||
fun <T> T.requireEqual(other: T) =
|
||||
requireEqual(this, other)
|
@@ -4,6 +4,8 @@ import blitz.collections.I2HashMapKey
|
||||
import blitz.collections.contents
|
||||
import kotlin.test.Test
|
||||
|
||||
// TODO: port tests to sanetest
|
||||
|
||||
class Maps {
|
||||
@Test
|
||||
fun i2hashmap() {
|
||||
|
Reference in New Issue
Block a user