
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