commonMain.ru.casperix.math.geometry.float32.Geometry2Float.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of math Show documentation
Show all versions of math Show documentation
Simple set of geometric and other types
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))
}
}