commonMain.ru.casperix.math.intersection.LineIntersection.kt Maven / Gradle / Ivy
package ru.casperix.math.intersection
import ru.casperix.math.axis_aligned.int32.Axis3i
import ru.casperix.math.axis_aligned.float64.Box3d
import ru.casperix.math.geometry.Line3d
import ru.casperix.math.geometry.Quad3d
import ru.casperix.math.geometry.Triangle3d
import ru.casperix.math.geometry.delta
import ru.casperix.math.vector.float64.Vector3d
fun intersectionLineWithTriangle(ray: Line3d, triangle: Triangle3d): Vector3d? {
val edge1 = triangle.v1 - triangle.v0
val edge2 = triangle.v2 - triangle.v0
val direction = ray.delta()
val pVec = direction.cross(edge2)
val determinant = edge1.dot(pVec)
if (determinant == 0.0) {
return null
}
val tVec = ray.v0 - triangle.v0
val bv = tVec.dot(pVec) / determinant
if (bv < 0 || bv > 1.0) {
return null
}
val qVec = tVec.cross(edge1)
val bw = direction.dot(qVec) / determinant;
if (bw < 0 || bv + bw > 1.0) {
return null
}
//check if the distance is longer than the predefined length.
val distance = edge2.dot(qVec) / determinant;
if (0.0 > distance || distance > 1.0 || !distance.isFinite()) {
return null
}
return ray.v0 + (direction * distance)
}
fun intersectionLineWithQuad(ray: Line3d, quad: Quad3d): Vector3d? {
for (partId in 0..1) {
val part = intersectionLineWithTriangle(ray, quad.getFace(partId))
if (part != null) return part
}
return null
}
data class FaceIntersection(val side: Axis3i, val point: Vector3d)
class BoxIntersection(val line: Line3d, val faces: List)
fun intersectionLineWithBox(ray: Line3d, box: Box3d): BoxIntersection? {
val isFirstInside = box.isInside(ray.v0)
val isLastInside = box.isInside(ray.v1)
val faces = mutableListOf()
if (isFirstInside && isLastInside) return BoxIntersection(ray, faces)
val maxIntersectionAmount = if (isFirstInside != isLastInside) 1 else 2
val list = ArrayList()
for (faceId in Axis3i.values()) {
val intersection = intersectionLineWithQuad(ray, box.getSideByDirection(faceId))
if (intersection != null) {
list.add(intersection)
faces.add(FaceIntersection(faceId, intersection))
if (list.size == maxIntersectionAmount) {
break
}
}
}
val intersectionCount = list.size
if ((maxIntersectionAmount == 1 && intersectionCount != 1) || (intersectionCount > 2)) {
throw Error("Invalid intersection count: $intersectionCount; must be not bigger than: $maxIntersectionAmount; for $ray")
}
if (intersectionCount == 0) {
return null
} else if (intersectionCount == 1) {
if (isFirstInside) {
return BoxIntersection(Line3d(ray.v0, list[0]), faces)
} else if (isLastInside) {
return BoxIntersection(Line3d(ray.v1, list[0]), faces)
} else {
return BoxIntersection(Line3d(list[0], list[0]), faces)
}
}
return BoxIntersection(Line3d(list[0], list[1]), faces)
}