All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.korlibs.math.geom.Matrix3.kt Maven / Gradle / Ivy

The newest version!
package korlibs.math.geom

import kotlin.math.*

/**
 * Useful for representing rotations and scales.
 */
data class Matrix3 private constructor(
    internal val data: FloatArray,
) {
    override fun equals(other: Any?): Boolean = other is Matrix3 && this.data.contentEquals(other.data)
    override fun hashCode(): Int = data.contentHashCode()

    private constructor(
        v00: Float, v10: Float, v20: Float,
        v01: Float, v11: Float, v21: Float,
        v02: Float, v12: Float, v22: Float,
    ) : this(
        floatArrayOf(
            v00, v10, v20,
            v01, v11, v21,
            v02, v12, v22,
        )
    )

    init {
        check(data.size == 9)
    }

    val v00: Float get() = data[0]
    val v10: Float get() = data[1]
    val v20: Float get() = data[2]
    val v01: Float get() = data[3]
    val v11: Float get() = data[4]
    val v21: Float get() = data[5]
    val v02: Float get() = data[6]
    val v12: Float get() = data[7]
    val v22: Float get() = data[8]

    val c0: Vector3 get() = Vector3.fromArray(data, 0)
    val c1: Vector3 get() = Vector3.fromArray(data, 3)
    val c2: Vector3 get() = Vector3.fromArray(data, 6)
    fun c(column: Int): Vector3 {
        if (column < 0 || column >= 3) error("Invalid column $column")
        return Vector3.fromArray(data, column * 3)
    }

    val r0: Vector3 get() = Vector3(v00, v01, v02)
    val r1: Vector3 get() = Vector3(v10, v11, v12)
    val r2: Vector3 get() = Vector3(v20, v21, v22)

    fun v(index: Int): Float = data[index]

    fun r(row: Int): Vector3 = when (row) {
        0 -> r0
        1 -> r1
        2 -> r2
        else -> error("Invalid row $row")
    }

    operator fun get(row: Int, column: Int): Float {
        if (column !in 0..2 || row !in 0..2) error("Invalid index $row,$column")
        return data[row * 3 + column]
    }

    fun transform(v: Vector3): Vector3 = Vector3(r0.dot(v), r1.dot(v), r2.dot(v))

    operator fun unaryMinus(): Matrix3 = Matrix3(
        -v00, -v10, -v20,
        -v01, -v11, -v21,
        -v02, -v12, -v22,
    )
    operator fun unaryPlus(): Matrix3 = this

    operator fun minus(other: Matrix3): Matrix3 = Matrix3(
        v00 - other.v00, v10 - other.v10, v20 - other.v20,
        v01 - other.v01, v11 - other.v11, v21 - other.v21,
        v02 - other.v02, v12 - other.v12, v22 - other.v22,
    )
    operator fun plus(other: Matrix3): Matrix3 = Matrix3(
        v00 + other.v00, v10 + other.v10, v20 + other.v20,
        v01 + other.v01, v11 + other.v11, v21 + other.v21,
        v02 + other.v02, v12 + other.v12, v22 + other.v22,
    )

    operator fun times(other: Matrix3): Matrix3 = Matrix3.multiply(this, other)
    operator fun times(scale: Float): Matrix3 = Matrix3(
        v00 * scale, v10 * scale, v20 * scale,
        v01 * scale, v11 * scale, v21 * scale,
        v02 * scale, v12 * scale, v22 * scale,
    )
    operator fun div(scale: Float): Matrix3 = this * (1f / scale)

    fun inv(): Matrix3 = inverted()

    val determinant: Float get() = v00 * (v11 * v22 - v21 * v12) -
        v01 * (v10 * v22 - v12 * v20) +
        v02 * (v10 * v21 - v11 * v20)

    fun inverted(): Matrix3 {
        val determinant = this.determinant

        if (determinant == 0.0f) throw ArithmeticException("Matrix is not invertible")

        val invDet = 1.0f / determinant

        return fromRows(
            (v11 * v22 - v21 * v12) * invDet,
            (v02 * v21 - v01 * v22) * invDet,
            (v01 * v12 - v02 * v11) * invDet,
            (v12 * v20 - v10 * v22) * invDet,
            (v00 * v22 - v02 * v20) * invDet,
            (v10 * v02 - v00 * v12) * invDet,
            (v10 * v21 - v20 * v11) * invDet,
            (v20 * v01 - v00 * v21) * invDet,
            (v00 * v11 - v10 * v01) * invDet,
        )
    }

    override fun toString(): String = buildString {
        append("Matrix3(\n")
        for (row in 0 until 3) {
            append("  [ ")
            for (col in 0 until 3) {
                if (col != 0) append(", ")
                val v = get(row, col)
                if (floor(v) == v) append(v.toInt()) else append(v)
            }
            append(" ],\n")
        }
        append(")")
    }

    fun transposed(): Matrix3 = Matrix3.fromColumns(r0, r1, r2)

    fun isAlmostEquals(other: Matrix3, epsilon: Float = 0.00001f): Boolean = c0.isAlmostEquals(other.c0, epsilon)
        && c1.isAlmostEquals(other.c1, epsilon)
        && c2.isAlmostEquals(other.c2, epsilon)

    companion object {
        const val M00 = 0
        const val M10 = 1
        const val M20 = 2

        const val M01 = 3
        const val M11 = 4
        const val M21 = 5

        const val M02 = 6
        const val M12 = 7
        const val M22 = 8

        const val M03 = 9
        const val M13 = 10
        const val M23 = 11

        val INDICES_BY_COLUMNS = intArrayOf(
            M00, M10, M20,
            M01, M11, M21,
            M02, M12, M22,
        )
        val INDICES_BY_ROWS = intArrayOf(
            M00, M01, M02,
            M10, M11, M12,
            M20, M21, M22,
        )

        val IDENTITY = Matrix3(
            1f, 0f, 0f,
            0f, 1f, 0f,
            0f, 0f, 1f,
        )

        fun fromRows(
            r0: Vector3, r1: Vector3, r2: Vector3
        ): Matrix3 = Matrix3(
            r0.x, r1.x, r2.x,
            r0.y, r1.y, r2.y,
            r0.z, r1.z, r2.z,
        )

        fun fromColumns(
            c0: Vector3, c1: Vector3, c2: Vector3
        ): Matrix3 = Matrix3(
            c0.x, c0.y, c0.z,
            c1.x, c1.y, c1.z,
            c2.x, c2.y, c2.z,
        )

        fun fromColumns(
            v00: Float, v10: Float, v20: Float,
            v01: Float, v11: Float, v21: Float,
            v02: Float, v12: Float, v22: Float,
        ): Matrix3 = Matrix3(
            v00, v10, v20,
            v01, v11, v21,
            v02, v12, v22,
        )

        fun fromRows(
            v00: Float, v01: Float, v02: Float,
            v10: Float, v11: Float, v12: Float,
            v20: Float, v21: Float, v22: Float,
        ): Matrix3 = Matrix3(
            v00, v10, v20,
            v01, v11, v21,
            v02, v12, v22,
        )

        fun multiply(l: Matrix3, r: Matrix3): Matrix3 = Matrix3.fromRows(
            (l.v00 * r.v00) + (l.v01 * r.v10) + (l.v02 * r.v20),
            (l.v00 * r.v01) + (l.v01 * r.v11) + (l.v02 * r.v21),
            (l.v00 * r.v02) + (l.v01 * r.v12) + (l.v02 * r.v22),

            (l.v10 * r.v00) + (l.v11 * r.v10) + (l.v12 * r.v20),
            (l.v10 * r.v01) + (l.v11 * r.v11) + (l.v12 * r.v21),
            (l.v10 * r.v02) + (l.v11 * r.v12) + (l.v12 * r.v22),

            (l.v20 * r.v00) + (l.v21 * r.v10) + (l.v22 * r.v20),
            (l.v20 * r.v01) + (l.v21 * r.v11) + (l.v22 * r.v21),
            (l.v20 * r.v02) + (l.v21 * r.v12) + (l.v22 * r.v22),
        )
    }
}

fun Matrix4.toMatrix3(): Matrix3 = Matrix3.fromRows(
    v00, v01, v02,
    v10, v11, v12,
    v20, v21, v22
)

fun Matrix3.toMatrix4(): Matrix4 = Matrix4.fromRows(
    v00, v01, v02, 0f,
    v10, v11, v12, 0f,
    v20, v21, v22, 0f,
    0f, 0f, 0f, 1f,
)

fun Matrix3.toQuaternion(): Quaternion = Quaternion.fromRotationMatrix(this)




© 2015 - 2025 Weber Informatics LLC | Privacy Policy