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

commonMain.casperix.math.geometry.float32.Polygon2f.kt Maven / Gradle / Ivy

package casperix.math.geometry.float32

import casperix.math.angle.float32.DegreeFloat
import casperix.math.collection.getLooped
import casperix.math.curve.float32.LineCurve2f
import casperix.math.geometry.*
import casperix.math.intersection.float32.Intersection2Float
import casperix.math.vector.float32.Vector2f
import casperix.math.vector.rotateCCW
import casperix.math.vector.rotateCW

fun Polygon2f.getVertexAngle(index: Int): DegreeFloat {
    val vertices = getVertices()
    val last = vertices.getLooped(index - 1)
    val current = vertices.getLooped(index)
    val next = vertices.getLooped(index + 1)

    return DegreeFloat.betweenDirections(last - current, next - current)
}

@Deprecated(message = "Its not good precision")
fun Polygon2f.isConvex(): Boolean {
    val points = getVertices()
    if (points.size <= 3) return true

    val pads2 = points.indices.map {
        val a = points[it]
        val b = points[(it + 1) % points.size]
        val c = points[(it + 2) % points.size]

        Geometry2Float.getPointAroundRay(a, b, c, 0f)
    }.toMutableSet()
    pads2.remove(PointAroundRay.INSIDE)
    if (pads2.size != 1) {
        return false
    }

    val edges = getEdgeList()

    edges.forEachIndexed { index, edgeA ->
        (0 until index).forEach { edgeIndex ->
            val template = edges[edgeIndex]
            val edgeB = (LineCurve2f(template).grow(-template.length() / 1000f) as LineCurve2f).line
            val pivot = Intersection2Float.getSegmentWithSegment(edgeA, edgeB)
            if (pivot != null) {
                return false
            }
        }

    }
    return true
}

fun Polygon2f.grow(value: Float): Polygon2f {
    val vertices = getVertices()
    if (vertices.size <= 2 || value == 0f) {
        return this
    }

    val rd = getWindingOrder()
    val factor = value * (if (rd == RotateDirection.CLOCKWISE) 1f else -1f)

    return CustomPolygon(vertices.mapIndexed { index, main ->
        val edgeA = Line(main, vertices.getLooped(index - 1))
        val edgeB = Line(main, vertices.getLooped(index + 1))

        val normalA = edgeA.delta().rotateCW().normalize()
        val normalB = edgeB.delta().rotateCCW().normalize()

        if ((normalA - normalB).length() < 0.01f) {
            //  if edge approximately parallel... use median normal
            val normal = (normalA + normalB).normalize()
            main + normal * factor
        } else {
            //  another case, search edge(with offset) intersection
            val lineA = edgeA.convert { it + normalA * factor }
            val lineB = edgeB.convert { it + normalB * factor }

            val candidate = Intersection2Float.getLineWithLine(lineA, lineB)
            candidate ?: edgeA.v0
        }
    })
}

fun Polygon2f.median(): Vector2f {
    val vertices = getVertices()
    return vertices.reduce { a, b -> a + b } / vertices.size.toFloat()
}

fun Polygon2f.toCustomPolygon2f(): CustomPolygon2f {
    if (this is CustomPolygon2f) {
        return this
    }
    return CustomPolygon(getVertices())
}

fun Polygon2f.scale(factor: Float): Polygon2f {
    return convert { it * factor }
}

fun Polygon2f.setWindingOrder(order: RotateDirection): Polygon2f {
    if (getWindingOrder() == order) return this
    return CustomPolygon(getVertices().asReversed())
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy