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

io.rtron.math.transform.Affine3D.kt Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2019-2022 Chair of Geoinformatics, Technical University of Munich
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.rtron.math.transform

import io.rtron.math.geometry.euclidean.threed.Pose3D
import io.rtron.math.geometry.euclidean.threed.Rotation3D
import io.rtron.math.geometry.euclidean.threed.point.Vector3D
import io.rtron.math.geometry.euclidean.threed.point.toVector3D
import io.rtron.math.geometry.euclidean.threed.surface.Polygon3D
import io.rtron.math.linear.RealMatrix
import io.rtron.math.linear.RealVector
import org.joml.Matrix4d as JOMLMatrix4d
import org.joml.Matrix4dc as JOMLMatrix4dc
import org.joml.Quaterniond as JOMLQuaterniond
import org.joml.Vector3d as JOMLVector3d

fun JOMLMatrix4dc.toRealMatrix(): RealMatrix {
    val values = DoubleArray(16)
    return RealMatrix(this.get(values), 4)
}

/**
 * Affine transformation matrix and operations in 3D.
 *
 * @param _matrix internal matrix of adapting library
 */
class Affine3D(
    private val _matrix: JOMLMatrix4dc
) : AbstractAffine() {

    // Properties and Initializers
    init {
        require(_matrix.isAffine) { "Matrix must be affine." }
        require(_matrix.isFinite) { "Matrix must contain only finite values." }
    }

    private val _matrixTransposed by lazy { JOMLMatrix4d(_matrix).transpose() }
    private val _matrixInverse by lazy { JOMLMatrix4d(_matrix).invertAffine() }

    // Methods: Transformation
    fun transform(point: Vector3D) = _matrix.transformPosition(point.toVector3DJOML()).toVector3D()
    fun inverseTransform(point: Vector3D) = _matrixInverse.transformPosition(point.toVector3DJOML()).toVector3D()

    @kotlin.jvm.JvmName("transformOfListVector3D")
    fun transform(points: List): List = points.map { transform(it) }

    @kotlin.jvm.JvmName("inverseTransformOfListVector3D")
    fun inverseTransform(points: List) = points.map { inverseTransform(it) }

    fun transform(polygon: Polygon3D) =
        Polygon3D(polygon.vertices.map { transform(it) }, polygon.tolerance)
    fun inverseTransform(polygon: Polygon3D) =
        Polygon3D(polygon.vertices.map { inverseTransform(it) }, polygon.tolerance)

    @kotlin.jvm.JvmName("transformOfListPolygon3D")
    fun transform(polygons: List) = polygons.map { transform(it) }

    @kotlin.jvm.JvmName("inverseTransformOfListPolygon3D")
    fun inverseTransform(polygons: List) = polygons.map { inverseTransform(it) }

    // Methods: Extraction

    /**
     * Extracts the translation vector of the [Affine3D] transformation matrix.
     *
     * @return translation vector
     */
    fun extractTranslation() = _matrix.getTranslation(JOMLVector3d()).toVector3D()

    /**
     * Extracts the scale vector of the [Affine3D] transformation matrix.
     * See the wikipedia article of [scaling (geometry)](https://en.wikipedia.org/wiki/Scaling_(geometry)).
     *
     * @return scaling vector
     */
    fun extractScaling(): RealVector = _matrix.getScale(JOMLVector3d()).toRealVector()

    /**
     * Extracts the rotation of the [Affine3D] transformation matrix.
     *
     * @return rotation
     */
    fun extractRotationAffine(): Affine3D {
        val rotation = _matrix.getUnnormalizedRotation(JOMLQuaterniond())!!
        return Affine3D(JOMLMatrix4d().rotate(rotation))
    }

    /**
     * Extracts the rotation of the [Affine3D] transformation matrix to a [Rotation3D].
     *
     * @return rotation angles
     */
    fun extractRotation(): Rotation3D {
        val rotationQuaternion = _matrix.getUnnormalizedRotation(JOMLQuaterniond())!!
        val rotationEuler = rotationQuaternion.getEulerAnglesXYZ(JOMLVector3d())!!
        return Rotation3D(rotationEuler.z, rotationEuler.y, rotationEuler.x)
    }

    /**
     * Appends an[other] [Affine3D] transformation matrix.
     *
     * @param other transformation matrix for appending
     * @return new [Affine3D] transformation matrix
     */
    fun append(other: Affine3D): Affine3D {
        val result = this.toMatrix4JOML().mul(other.toMatrix4JOML())
        return Affine3D(result)
    }

    // Methods: Generated
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false

        other as Affine3D

        if (_matrix != other._matrix) return false

        return true
    }

    override fun hashCode(): Int {
        return _matrix.hashCode()
    }

    // Conversions
    fun toMatrix4JOML() = JOMLMatrix4d(this._matrix)
    fun toRealMatrix() = this._matrixTransposed.toRealMatrix()
    fun toDoubleArray(): DoubleArray = this._matrixTransposed.get(DoubleArray(16))
    fun toDoubleList(): List = toDoubleArray().toList()

    override fun toString(): String {
        return "Affine3D(_matrix=$_matrix)"
    }

    companion object {
        val UNIT = Affine3D(JOMLMatrix4d())

        /**
         * Creates an [Affine3D] transformation matrix from a 4x4 [RealMatrix].
         */
        fun of(matrix: RealMatrix): Affine3D {
            require(matrix.dimension == 4 to 4) { "Matrix must have dimensions of 4x4." }

            val jomlMatrix = JOMLMatrix4d().set(matrix.transpose().toDoubleArray())
            return Affine3D(jomlMatrix)
        }

        /**
         * Creates an [Affine3D] transformation matrix from a [translation] vector.
         */
        fun of(translation: Vector3D): Affine3D {
            val matrix = JOMLMatrix4d().translation(translation.toVector3DJOML())
            return Affine3D(matrix)
        }

        /**
         * Creates an [Affine3D] transformation matrix from a [scaling] vector.
         */
        fun of(scaling: RealVector): Affine3D {
            require(scaling.dimension == 3) { "Wrong dimension ${scaling.dimension}." }
            val matrix = JOMLMatrix4d().scale(scaling[0], scaling[1], scaling[2])
            return Affine3D(matrix)
        }

        /**
         * Creates an [Affine3D] transformation matrix from a [rotation].
         */
        fun of(rotation: Rotation3D): Affine3D {
            val matrix = JOMLMatrix4d()
                .rotateZ(rotation.heading)
                .rotateY(rotation.pitch)
                .rotateX(rotation.roll)

            return Affine3D(matrix)
        }

        /**
         * Creates an [Affine3D] transformation matrix from a list of sequentially applied affine
         * transformation matrices.
         */
        fun of(affineList: List): Affine3D {
            return affineList.fold(UNIT) { acc, affine -> acc.append(affine) }
        }

        /**
         * Creates an [Affine3D] transformation matrix from a list of sequentially applied affine
         * transformation matrices.
         */
        fun of(vararg affines: Affine3D) = of(affines.asList())

        /**
         * Creates an [Affine3D] transformation matrix from a [pose].
         */
        fun of(pose: Pose3D): Affine3D {
            val translationAffine = of(pose.point)
            val rotationAffine = of(pose.rotation)
            return of(listOf(translationAffine, rotationAffine))
        }

        /**
         * Creates an [Affine3D] transformation matrix by means of a new coordinate system basis.
         *
         * @param basisX x axis of new basis
         * @param basisY y axis of new basis
         * @param basisZ z axis of new basis
         */
        fun of(basisX: Vector3D, basisY: Vector3D, basisZ: Vector3D): Affine3D {
            val matrix = JOMLMatrix4d().set(
                basisX.toVector4DJOML(),
                basisY.toVector4DJOML(),
                basisZ.toVector4DJOML(),
                Vector3D.ZERO.toVector4DJOML()
            )!!
            return Affine3D(matrix.invertAffine())
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy