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

commonMain.ru.casperix.math.geometry.float32.Geometry2Float.kt Maven / Gradle / Ivy

package ru.casperix.math.geometry.float32

import ru.casperix.math.geometry.GeometryApi
import ru.casperix.math.geometry.*
import ru.casperix.math.intersection.float32.Intersection2Float
import ru.casperix.math.straight_line.float32.LineSegment2f
import ru.casperix.math.vector.float32.Vector2f
import kotlin.math.*


object Geometry2Float  : GeometryApi {

    fun distPointToSegment(point: Vector2f, segment: LineSegment2f): Float {
        return distPointToSegment(point, segment.toLine())
    }

    fun projectionByLine(source: Vector2f, segment: Line2f): Vector2f {
        val direction = segment.delta()
        return segment.v0 + projectionByDirection(source - segment.v0, direction)
    }

    fun projectionBySegment(source: Vector2f, segment: Line2f): Vector2f? {
        val direction = segment.delta()
        val length = projectionByDirectionLength(source - segment.v0, direction)
        if (length < 0 || length > direction.length()) return null
        return segment.v0 + direction.normalize() * length
    }

    fun projectionBySegmentAsSegment(source: Vector2f, segment: Line2f): Line2f? {
        val projection = projectionBySegment(source, segment) ?: return null
        return Line2f(source, projection)
    }

    fun projectionByLineAsSegment(source: Vector2f, segment: Line2f): Line2f {
        val projection = projectionByLine(source, segment)
        return Line2f(source, projection)
    }

    fun projectionBySegmentAsSegment(source: Vector2f, segmentStart: Vector2f, segmentFinish: Vector2f): Line2f? {
        return projectionBySegmentAsSegment(source, Line2f(segmentStart, segmentFinish))
    }

    fun projectionByLineAsSegment(source: Vector2f, segmentStart: Vector2f, segmentFinish: Vector2f): Line2f {
        return projectionByLineAsSegment(source, Line2f(segmentStart, segmentFinish))
    }

    override fun getPointAroundRay(
        start: Vector2f,
        finish: Vector2f,
        point: Vector2f,
        error: Float
    ): PointAroundRay {
        val D = calculateDeterminant(start, finish, point)
        if (D > error) return PointAroundRay.LEFT
        if (D < -error) return PointAroundRay.RIGHT
        return PointAroundRay.INSIDE
    }

    override fun calculateDeterminant(start: Vector2f, finish: Vector2f, point: Vector2f): Float {
        return (finish.x - start.x) * (point.y - start.y) - (finish.y - start.y) * (point.x - start.x)
    }

    override fun distPointToQuad(P: Vector2f, quad: Quad2f): Float {
        if (Intersection2Float.hasPointWithQuad(P, quad)) return 0f

        return quad.getEdgeList().minOf { edge ->
            distPointToSegment(P, edge)
        }
    }

    override fun distPointToLine(P: Vector2f, T: Line2f): Float {
        val x0 = P.x
        val y0 = P.y

        val x1 = T.v0.x
        val y1 = T.v0.y
        val x2 = T.v1.x
        val y2 = T.v1.y

        val f = (x2 - x1) * (y1 - y0) - (x1 - x0) * (y2 - y1)

        val d = sqrt((x2 - x1).pow(2) + (y2 - y1).pow(2))

        if (d == 0f) {
            return P.distTo(T.v0)
        }
        return (f.absoluteValue / d)
    }



    /**
     * https://www.geeksforgeeks.org/minimum-distance-from-a-point-to-the-line-segment-using-vectors/
     */
    override fun distPointToSegment(P: Vector2f, A: Vector2f, B: Vector2f): Float {
        val AB = B - A
        val BP = P - B
        val AP = P - A

        // Variables to store dot product
        // Calculating the dot product
        val AB_BP = AB.dot(BP)
        val AB_AP = AB.dot(AP)

        // Minimum distance from
        // point E to the line segment
        return if (AB_BP > 0) {
            // Case 1
            val x = P.x - B.x
            val y = P.y - B.y
            sqrt(x * x + y * y)
        } else if (AB_AP < 0) {
            // Case 2
            val x = P.x - A.x
            val y = P.y - A.y
            sqrt(x * x + y * y)
        } else {
            // Case 3
            // Finding the perpendicular distance
            val x1 = AB.x
            val y1 = AB.y
            val x2 = AP.x
            val y2 = AP.y
            val mod = sqrt(x1 * x1 + y1 * y1)
            abs(x1 * y2 - y1 * x2) / mod
        }
    }

    override fun projectionByDirection(source: Vector2f, direction: Vector2f): Vector2f {
        val normalized = direction.normalize()
        val length = source.dot(normalized)
        return normalized * length
    }

    override fun projectionByDirectionLength(source: Vector2f, direction: Vector2f): Float {
        val normalized = direction.normalize()
        return source.dot(normalized)
    }

    fun closestSegmentPoint(line: Line2f, target: Vector2f): Vector2f {
        val lineLength = line.length()
        val lineDirection = line.delta()
        val localOffset = Geometry2Float.projectionByDirectionLength(target - line.v0, lineDirection)

        return if (localOffset < 0f) {
            line.v0
        } else if (localOffset <= lineLength) {
            line.v0 + lineDirection * localOffset / lineLength
        } else {
            line.v1
        }
    }

    fun closestConnectionBetweenSegments(aSegment: Line2f, bSegment: Line2f): Line2f {
        val pivot = Intersection2Float.getSegmentWithSegment(aSegment, bSegment)
        if (pivot != null) {
            return Line2f(pivot, pivot)
        }

        val (l1, d1) = nearToSegmentAndDist(aSegment, bSegment.v0)
        val (l2, d2) = nearToSegmentAndDist(aSegment, bSegment.v1)
        val (l3, d3) = nearToSegmentAndDist(bSegment, aSegment.v0)
        val (l4, d4) = nearToSegmentAndDist(bSegment, aSegment.v1)

        val d = minOf(d1, d2, d3, d4)

        if (d == d1) {
            return l1
        } else if (d == d2) {
            return l2
        } else if (d == d3) {
            return l3
        } else {
            return l4
        }
    }

    private fun nearToSegmentAndDist(line: Line2f, point: Vector2f): Pair {
        val result = closestSegmentPoint(line, point)
        return Pair(Line2f(point, result), result.distTo(point))
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy