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

gov.nasa.worldwind.geom.Line Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2012 United States Government as represented by the Administrator of the
 * National Aeronautics and Space Administration.
 * All Rights Reserved.
 */
package gov.nasa.worldwind.geom;

import gov.nasa.worldwind.util.Logging;

/**
 * @author Tom Gaskins
 * @version $Id: Line.java 1171 2013-02-11 21:45:02Z dcollins $
 */
public final class Line// Instances are immutable
{
    private final Vec4 origin;
    private final Vec4 direction;

    /**
     * Create the line containing a line segement between two points.
     *
     * @param pa the first point of the line segment.
     * @param pb the second point of the line segment.
     *
     * @return The line containing the two points.
     *
     * @throws IllegalArgumentException if either point is null or they are coincident.
     */
    public static Line fromSegment(Vec4 pa, Vec4 pb)
    {
        return new Line(pa, new Vec4(pb.x - pa.x, pb.y - pa.y, pb.z - pa.z, 0));
    }

    /**
     * @param origin    the origin of the line being constructed
     * @param direction the direction of the line being constructed
     *
     * @throws IllegalArgumentException if origin is null, or direction is null or has zero
     *                                  length
     */
    public Line(Vec4 origin, Vec4 direction)
    {
        String message = null;
        if (origin == null)
            message = "nullValue.OriginIsNull";
        else if (direction == null)
            message = "nullValue.DirectionIsNull";
        else if (direction.getLength3() <= 0)
            message = "Geom.Line.DirectionIsZeroVector";
        if (message != null)
        {
            message = Logging.getMessage(message);
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.origin = origin;
        this.direction = direction;
    }

    public final Vec4 getDirection()
    {
        return direction;
    }

    public final Vec4 getOrigin()
    {
        return origin;
    }

    public final Vec4 getPointAt(double t)
    {
        return Vec4.fromLine3(this.origin, t, this.direction);
    }

    public final double selfDot()
    {
        return this.origin.dot3(this.direction);
    }

    /**
     * Performs a comparison to test whether this Object is internally identical to the other Object o.
     * This method takes into account both direction and origin, so two lines which may be equivalent may not be
     * considered equal.
     *
     * @param o the object to be compared against.
     *
     * @return true if these two objects are equal, false otherwise
     */
    @Override
    public final boolean equals(Object o)
    {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;

        final gov.nasa.worldwind.geom.Line line = (gov.nasa.worldwind.geom.Line) o;

        if (!direction.equals(line.direction))
            return false;
        //noinspection RedundantIfStatement
        if (!line.origin.equals(origin))
            return false;

        return true;
    }

    @Override
    public final int hashCode()
    {
        int result;
        result = origin.hashCode();
        result = 29 * result + direction.hashCode();
        return result;
    }

    public String toString()
    {
        return "Origin: " + this.origin + ", Direction: " + this.direction;
    }
//
//    public final double distanceToOld(Vec4 p)
//    {
//        if (p == null)
//        {
//            String message = Logging.getMessage("nullValue.PointIsNull");
//            Logging.logger().severe(message);
//            throw new IllegalArgumentException(message);
//        }
//
//        Vec4 origin = this.origin;
//        Vec4 sideB = origin.subtract3(p); // really a vector
//
//        double distanceToOrigin = sideB.dot3(this.direction);
//        double divisor = distanceToOrigin / this.direction.getLengthSquared3();
//
//        Vec4 sideA = this.direction.multiply3(divisor);
//
//        double aSquared = sideA.getLengthSquared3();
//        double bSquared = sideB.getLengthSquared3();
//
//        return Math.sqrt(bSquared - aSquared);
//    }

    public final Vec4 nearestPointTo(Vec4 p)
    {
        Vec4 w = p.subtract3(this.origin);

        double c1 = w.dot3(this.direction);
        double c2 = this.direction.dot3(this.direction);

        return this.origin.add3(this.direction.multiply3(c1 / c2));
    }

    /**
     * Calculate the shortests distance between this line and a specified Vec4. This method returns a
     * positive distance.
     *
     * @param p the Vec4 whose distance from this Line will be calculated
     *
     * @return the distance between this Line and the specified Vec4
     *
     * @throws IllegalArgumentException if p is null
     */
    public final double distanceTo(Vec4 p)
    {
        return p.distanceTo3(this.nearestPointTo(p));
    }

    /**
     * Finds the closest point to a third point of a segment defined by two points.
     *
     * @param p0 The first endpoint of the segment.
     * @param p1 The second endpoint of the segment.
     * @param p  The point outside the segment whose closest point on the segment is desired.
     *
     * @return The closest point on (p0, p1) to p. Note that this will be p0 or p1 themselves whenever the closest
     *         point on the line defined by p0 and p1 is outside the segment (i.e., the results are bounded by
     *         the segment endpoints).
     */
    public static Vec4 nearestPointOnSegment(Vec4 p0, Vec4 p1, Vec4 p)
    {
        Vec4 v = p1.subtract3(p0);
        Vec4 w = p.subtract3(p0);

        double c1 = w.dot3(v);
        double c2 = v.dot3(v);

        if (c1 <= 0)
            return p0;
        if (c2 <= c1)
            return p1;

        return p0.add3(v.multiply3(c1 / c2));
    }

    public static double distanceToSegment(Vec4 p0, Vec4 p1, Vec4 p)
    {
        Vec4 pb = nearestPointOnSegment(p0, p1, p);

        return p.distanceTo3(pb);
    }

    /**
     * Clip a line segment to a frustum, returning the end points of the portion of the segment that is within the
     * frustum.
     *
     * @param pa      the first point of the segment.
     * @param pb      the second point of the segment.
     * @param frustum the frustum.
     *
     * @return The two points at which the segment intersects the frustum, or null if the segment does not intersect and
     *         the frustum does not fully contain it. If the segment is coincident with a plane of the frustum, the
     *         returned segment is the portion of the original segment on that plane, clipped to the other frustum
     *         planes.
     */
    public static Vec4[] clipToFrustum(Vec4 pa, Vec4 pb, Frustum frustum)
    {
        return clipToFrustum(pa, pb, frustum, 1);
    }

    private static Vec4[] clipToFrustum(Vec4 pa, Vec4 pb, Frustum frustum, int maxRecursionCount)
    {
        if (pa == null || pb == null)
        {
            String message = Logging.getMessage("nullValue.PointIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        if (frustum == null)
        {
            String message = Logging.getMessage("nullValue.FrustumIsNull");

            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        // First do a trivial accept test.
        if (frustum.contains(pa) && frustum.contains(pb))
            return new Vec4[] {pa, pb};

        Vec4[] segment = new Vec4[] {pa, pb};
        Vec4[] ipts;

        for (Plane p : frustum.getAllPlanes())
        {
            // See if both points are behind the plane and therefore not in the frustum.
            if (p.onSameSide(segment[0], segment[1]) < 0)
                return null;

            // Clip the segment to the plane if they intersect.
            ipts = p.clip(segment[0], segment[1]);
            if (ipts != null)
            {
                segment = ipts;
            }
        }

        // If one of the initial points was in the frustum then the segment must have been clipped.
        if (frustum.contains(pa) || frustum.contains(pb))
            return segment;

        // The segment was clipped by an infinite frustum plane but may still lie outside the frustum.
        // So recurse using the clipped segment.
        if (maxRecursionCount > 0)
            return clipToFrustum(segment[0], segment[1], frustum, --maxRecursionCount);
        else
            return segment;
    }

    /**
     * Determine if a point is behind the Line's origin.
     *
     * @param point The point to test.
     *
     * @return true if point is behind this Line's origin, false otherwise.
     */
    public boolean isPointBehindLineOrigin(Vec4 point)
    {
        double dot = point.subtract3(this.getOrigin()).dot3(this.getDirection());
        return dot < 0.0;
    }

    public Vec4 nearestIntersectionPoint(Intersection[] intersections)
    {
        Vec4 intersectionPoint = null;

        // Find the nearest intersection that's in front of the ray origin.
        double nearestDistance = Double.MAX_VALUE;
        for (Intersection intersection : intersections)
        {
            // Ignore any intersections behind the line origin.
            if (!this.isPointBehindLineOrigin(intersection.getIntersectionPoint()))
            {
                double d = intersection.getIntersectionPoint().distanceTo3(this.getOrigin());
                if (d < nearestDistance)
                {
                    intersectionPoint = intersection.getIntersectionPoint();
                    nearestDistance = d;
                }
            }
        }

        return intersectionPoint;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy