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

commonMain.earth.worldwind.geom.Line.kt Maven / Gradle / Ivy

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 - 2025 Weber Informatics LLC | Privacy Policy