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

io.rtron.math.geometry.euclidean.threed.surface.Polygon3D.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.geometry.euclidean.threed.surface

import com.github.kittinunf.result.NoException
import com.github.kittinunf.result.Result
import io.rtron.math.geometry.euclidean.threed.point.Vector3D
import io.rtron.math.linear.dimensionOfSpan
import io.rtron.math.processing.calculateNormal
import io.rtron.math.processing.isPlanar
import io.rtron.math.std.DEFAULT_TOLERANCE
import io.rtron.math.transform.AffineSequence3D
import io.rtron.std.noneWithNextEnclosing

/**
 * Planar polygon consisting of a list of [vertices].
 *
 * @param vertices vertices of the polygon must be located in a plane
 */
data class Polygon3D(
    val vertices: List = listOf(),
    override val tolerance: Double,
    override val affineSequence: AffineSequence3D = AffineSequence3D.EMPTY
) : AbstractSurface3D() {

    // Properties and Initializers
    private val numberOfVertices = vertices.size

    /** list of edges between the first vertex and all other vertices */
    private val innerEdges = vertices.filterIndexed { index, _ -> index != 0 }.map { it - vertices.first() }

    /** dimension of the polygon's span used to check whether the polygon is planar */
    private val dimensionSpan = innerEdges.map { it.toRealVector() }.dimensionOfSpan()

    init {
        require(numberOfVertices >= 3) { "Not enough vertices provided for constructing a polygon." }
        require(vertices.noneWithNextEnclosing { a, b -> a.fuzzyEquals(b, tolerance) }) { "Consecutively following point duplicates found." }
        require(dimensionSpan >= 2) { "The dimension of the span is too low ($dimensionSpan) which might be caused by colinear vertices." }
        require(vertices.isPlanar(tolerance)) { "The vertices of a polygon must be located in a plane." }
    }

    // Methods

    /** Returns the normal of the polygon. */
    fun getNormal(): Result =
        this.vertices.calculateNormal().normalized().let { Result.success(it) }

    /** Returns a new polygon with an opposite facing by reversing the vertices order */
    fun reversed() = Polygon3D(vertices.reversed(), tolerance, affineSequence)

    override fun calculatePolygonsLocalCS(): Result, NoException> = Result.success(listOf(this))

    // Conversions
    /** Returns the coordinates of all vertices as a flattened list */
    fun toVertexPositionElementList() = this.vertices.flatMap { it.toDoubleList() }

    companion object {
        val TETRAGON = of(
            Vector3D(-1.0, -1.0, 0.0),
            Vector3D(-1.0, 1.0, 0.0),
            Vector3D(1.0, 1.0, 0.0),
            Vector3D(1.0, -1.0, 0.0),
            tolerance = DEFAULT_TOLERANCE
        )

        /**
         * Constructs a polygon based on the [vectors].
         */
        fun of(vararg vectors: Vector3D, tolerance: Double) = Polygon3D(vectors.toList(), tolerance)

        /**
         * Constructs a polygon based on a [Triple] of [vectors].
         */
        fun of(vectors: Triple, tolerance: Double) =
            Polygon3D(vectors.toList(), tolerance)
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy