commonMain.earth.worldwind.geom.Line.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of worldwind-jvm Show documentation
Show all versions of worldwind-jvm Show documentation
The WorldWind Kotlin SDK (WWK) includes the library, examples and tutorials for building multiplatform 3D virtual globe applications for Android, Web and Java.
The newest version!
package earth.worldwind.geom
import earth.worldwind.util.Logger.ERROR
import earth.worldwind.util.Logger.logMessage
/**
* Represents a line in Cartesian coordinates.
*/
open class Line {
/**
* This line's origin.
*/
val origin = Vec3()
/**
* This line's direction.
*/
val direction = Vec3()
/**
* Constructs a line with origin and direction both zero.
*/
constructor()
/**
* Constructs a line with a specified origin and direction.
*
* @param origin the line's origin
* @param direction the line's direction
*/
constructor(origin: Vec3, direction: Vec3): this() { set(origin, direction) }
/**
* Constructs a line with the origin and direction from a specified line.
*
* @param line the line specifying origin and direction
*/
constructor(line: Line): this(line.origin, line.direction)
/**
* Sets this line to a specified origin and direction.
*
* @param origin the line's new origin
* @param direction the line's new direction
*
* @return this line, set to the new origin and direction
*/
fun set(origin: Vec3, direction: Vec3) = apply {
this.origin.copy(origin)
this.direction.copy(direction)
}
/**
* Sets this line to the specified segment. This line has its origin at the first endpoint and its direction
* extending from the first endpoint to the second.
*
* @param pointA the segment's first endpoint
* @param pointB the segment's second endpoint
*
* @return this line, set to the specified segment
*/
fun setToSegment(pointA: Vec3, pointB: Vec3) = apply {
origin.copy(pointA)
direction.set(pointB.x - pointA.x, pointB.y - pointA.y, pointB.z - pointA.z)
}
/**
* Computes a Cartesian point a specified distance along this line.
*
* @param distance The distance from this line's origin at which to compute the point.
* @param result A pre-allocated [Vec3] instance in which to return the computed point.
*
* @return The specified result argument containing the computed point.
*/
fun pointAt(distance: Double, result: Vec3): Vec3 {
result.x = origin.x + direction.x * distance
result.y = origin.y + direction.y * distance
result.z = origin.z + direction.z * distance
return result
}
/**
* Computes the first intersection of a triangle strip with this line. This line is interpreted as a ray;
* intersection points behind the line's origin are ignored.
*
* The triangle strip is specified by a list of vertex points and a list of elements indicating the triangle strip
* tessellation of those vertices. The triangle strip elements are interpreted in the same manner as OpenGL, where
* each index indicates a vertex position rather than an actual index into the points array (e.g. a triangle strip
* index of 1 indicates the XYZ tuple starting at array index 3).
*
* @param points an array of points containing XYZ tuples
* @param stride the number of coordinates between the first coordinate of adjacent points - must be at least 3
* @param elements an array of indices into the points defining the triangle strip organization
* @param count the number of indices to consider
* @param result a pre-allocated Vec3 in which to return the nearest intersection point, if any
*
* @return true if this line intersects the triangle strip, otherwise false
*
* @throws IllegalArgumentException If array is empty, if the stride is less than 3,
* if the count is less than 0
*/
fun triStripIntersection(points: FloatArray, stride: Int, elements: ShortArray, count: Int, result: Vec3): Boolean {
require(points.size >= stride) {
logMessage(ERROR, "Line", "triStripIntersection", "missingArray")
}
require(stride >= 3) {
logMessage(ERROR, "Line", "triStripIntersection", "invalidStride")
}
require(elements.isNotEmpty()) {
logMessage(ERROR, "Line", "triStripIntersection", "missingArray")
}
require(count >= 0) {
logMessage(ERROR, "Line", "triStripIntersection", "invalidCount")
}
// Taken from Moller and Trumbore
// http://www.cs.virginia.edu/~gfx/Courses/2003/ImageSynthesis/papers/Acceleration/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf
// Adapted from the original ray-triangle intersection algorithm to optimize for ray-triangle strip
// intersection. We optimize by reusing constant terms, replacing use of Vec3 with inline primitives, and
// exploiting the triangle strip organization to reuse computations common to adjacent triangles. These
// optimizations reduced worst-case terrain picking performance for Web WorldWind by approximately 50% in
// Chrome on a 2010 iMac and a Nexus 9.
val vx = direction.x
val vy = direction.y
val vz = direction.z
val sx = origin.x
val sy = origin.y
val sz = origin.z
var tMin = Double.POSITIVE_INFINITY
val epsilon = 0.00001
// Get the triangle strip's first vertex.
var vertex = elements[0] * stride
var vert1x = points[vertex++]
var vert1y = points[vertex++]
var vert1z = points[vertex]
// Get the triangle strip's second vertex.
vertex = elements[1] * stride
var vert2x = points[vertex++]
var vert2y = points[vertex++]
var vert2z = points[vertex]
// Compute the intersection of each triangle with the specified ray.
for (idx in 2 until count) {
// Move the last two vertices into the first two vertices. This takes advantage of the triangle strip's
// structure and avoids redundant reads from points and elements. During the first iteration this places the
// triangle strip's first three vertices in vert0, vert1 and vert2, respectively.
val vert0x = vert1x
val vert0y = vert1y
val vert0z = vert1z
vert1x = vert2x
vert1y = vert2y
vert1z = vert2z
// Get the triangle strip's next vertex.
vertex = elements[idx] * stride
vert2x = points[vertex++]
vert2y = points[vertex++]
vert2z = points[vertex]
// find vectors for two edges sharing point a: vert1 - vert0 and vert2 - vert0
val edge1x = vert1x - vert0x
val edge1y = vert1y - vert0y
val edge1z = vert1z - vert0z
val edge2x = vert2x - vert0x
val edge2y = vert2y - vert0y
val edge2z = vert2z - vert0z
// Compute cross product of line direction and edge2
val px = vy * edge2z - vz * edge2y
val py = vz * edge2x - vx * edge2z
val pz = vx * edge2y - vy * edge2x
// Get determinant
val det = edge1x * px + edge1y * py + edge1z * pz // edge1 dot p
// if det is near zero then ray lies in plane of triangle
if (det > -epsilon && det < epsilon) continue
val invDet = 1.0 / det
// Compute distance for vertex A to ray origin: origin - vert0
val tx = sx - vert0x
val ty = sy - vert0y
val tz = sz - vert0z
// Calculate u parameter and test bounds: 1/det * t dot p
val u = invDet * (tx * px + ty * py + tz * pz)
if (u < -epsilon || u > 1 + epsilon) continue
// Prepare to test v parameter: tvec cross edge1
val qx = ty * edge1z - tz * edge1y
val qy = tz * edge1x - tx * edge1z
val qz = tx * edge1y - ty * edge1x
// Calculate v parameter and test bounds: 1/det * dir dot q
val v = invDet * (vx * qx + vy * qy + vz * qz)
if (v < -epsilon || u + v > 1 + epsilon) continue
// Calculate the point of intersection on the line: t = 1/det * edge2 dot q
val t = invDet * (edge2x * qx + edge2y * qy + edge2z * qz)
if (t >= 0 && t < tMin) tMin = t
}
if (tMin != Double.POSITIVE_INFINITY) result.set(sx + vx * tMin, sy + vy * tMin, sz + vz * tMin)
return tMin != Double.POSITIVE_INFINITY
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Line) return false
return origin == other.origin && direction == other.direction
}
override fun hashCode(): Int {
var result = origin.hashCode()
result = 31 * result + direction.hashCode()
return result
}
override fun toString() = "Line(origin=$origin, direction=$direction)"
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy