gov.nasa.worldwind.geom.Sphere 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.View;
import gov.nasa.worldwind.render.*;
import gov.nasa.worldwind.util.*;
import com.jogamp.opengl.GL2;
import com.jogamp.opengl.glu.*;
/**
* Represents a sphere in three dimensional space. Instances of Sphere
are immutable.
*
* @author Tom Gaskins
* @version $Id: Sphere.java 1171 2013-02-11 21:45:02Z dcollins $
*/
public final class Sphere implements Extent, Renderable
{
public final static Sphere UNIT_SPHERE = new Sphere(Vec4.ZERO, 1);
protected final Vec4 center;
protected final double radius;
/**
* Creates a sphere that completely contains a set of points.
*
* @param points the Vec4
s to be enclosed by the new Sphere
*
* @return a Sphere
encompassing the given array of Vec4
s
*
* @throws IllegalArgumentException if points
is null or empty
*/
public static Sphere createBoundingSphere(Vec4 points[])
{
if (points == null)
{
String message = Logging.getMessage("nullValue.PointsArrayIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (points.length < 1)
{
String message = Logging.getMessage("Geom.Sphere.NoPointsSpecified");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// Creates the sphere around the axis aligned bounding box of the input points.
Vec4[] extrema = Vec4.computeExtrema(points);
Vec4 center = new Vec4(
(extrema[0].x + extrema[1].x) / 2.0,
(extrema[0].y + extrema[1].y) / 2.0,
(extrema[0].z + extrema[1].z) / 2.0);
double radius = extrema[0].distanceTo3(extrema[1]) / 2.0;
return new Sphere(center, radius);
}
/**
* Creates a sphere that completely contains a set of points.
*
* @param buffer the Cartesian coordinates to be enclosed by the new Sphere.
*
* @return a Sphere
encompassing the given coordinates.
*
* @throws IllegalArgumentException if buffer
is null or contains fewer than three values.
*/
public static Sphere createBoundingSphere(BufferWrapper buffer)
{
if (buffer == null)
{
String message = Logging.getMessage("nullValue.BufferIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (buffer.getBackingBuffer().position() > buffer.getBackingBuffer().limit() - 3)
{
String message = Logging.getMessage("Geom.Sphere.NoPointsSpecified");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// Creates the sphere around the axis aligned bounding box of the input points.
Vec4[] extrema = Vec4.computeExtrema(buffer);
Vec4 center = new Vec4(
(extrema[0].x + extrema[1].x) / 2.0,
(extrema[0].y + extrema[1].y) / 2.0,
(extrema[0].z + extrema[1].z) / 2.0);
double radius = extrema[0].distanceTo3(extrema[1]) / 2.0;
return new Sphere(center, radius);
}
/**
* Creates a sphere that completely contains a set of Extents. This returns null if the specified Iterable is empty
* or contains only null elements. Note that this does not compute an optimal bounding sphere. The returned sphere
* is computed as follows: the center is the mean of all center points in the specified set of Extents, and the
* radius is smallest value which defines a sphere originating at the computed center point and containing all the
* specified Extents.
*
* @param extents the extends to be enclosed by the new Sphere.
*
* @return a new Sphere encompassing the given Extents.
*
* @throws IllegalArgumentException if the Iterable is null.
*/
public static Sphere createBoundingSphere(Iterable extends Extent> extents)
{
if (extents == null)
{
String message = Logging.getMessage("nullValue.IterableIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
Vec4 center = null;
double radius = 0;
int count = 0;
// Compute the mean center point of the specified extents.
for (Extent e : extents)
{
if (e == null)
continue;
center = (center != null) ? e.getCenter().add3(center) : e.getCenter();
count++;
}
// If the accumulated center point is null, then the specified Iterable is empty or contains only null elements.
// We cannot compute an enclosing extent, so just return null.
if (center == null)
return null;
center = center.divide3(count);
// Compute the maximum distance from the mean center point to the outermost point on each extent. This is
// the radius of the enclosing extent.
for (Extent e : extents)
{
if (e == null)
continue;
double distance = e.getCenter().distanceTo3(center) + e.getRadius();
if (radius < distance)
radius = distance;
}
return new Sphere(center, radius);
}
/**
* Creates a new Sphere
from a given center and radius. radius
must be positive (that is,
* greater than zero), and center
may not be null.
*
* @param center the center of the new sphere
* @param radius the radius of the new sphere
*
* @throws IllegalArgumentException if center
is null or if radius
is non-positive
*/
public Sphere(Vec4 center, double radius)
{
if (center == null)
{
String message = Logging.getMessage("nullValue.CenterIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (radius <= 0)
{
String message = Logging.getMessage("Geom.Sphere.RadiusIsZeroOrNegative", radius);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
this.center = center;
this.radius = radius;
}
/**
* Obtains the radius of this Sphere
. The radus is the distance from the center to the surface. If an
* object's distance to this sphere's center is less than or equal to the radius, then that object is at least
* partially within this Sphere
.
*
* @return the radius of this sphere
*/
public final double getRadius()
{
return this.radius;
}
/**
* Obtains the diameter of this Sphere
. The diameter is twice the radius.
*
* @return the diameter of this Sphere
*/
public final double getDiameter()
{
return 2 * this.radius;
}
/**
* Obtains the center of this Sphere
.
*
* @return the Vec4
situated at the center of this Sphere
*/
public final Vec4 getCenter()
{
return this.center;
}
/** {@inheritDoc} */
public double getEffectiveRadius(Plane plane)
{
return this.getRadius();
}
/**
* Computes a point on the sphere corresponding to a specified location.
*
* @param location the location to compute the point for.
*
* @return the Cartesian coordinates of the corresponding location on the sphere.
*
* @throws IllegalArgumentException if the location is null.
*/
public Vec4 getPointOnSphere(LatLon location)
{
if (location == null)
{
String msg = Logging.getMessage("nullValue.LocationIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
double sinLat = location.getLatitude().sin();
double x = this.center.x + this.getRadius() * sinLat * location.getLongitude().cos();
double y = this.center.y + this.getRadius() * sinLat * location.getLongitude().sin();
double z = this.center.z + this.getRadius() * location.getLatitude().cos();
return new Vec4(x, y, z);
}
/**
* Obtains the intersections of this sphere with a line. The returned array may be either null or of zero length if
* no intersections are discovered. It does not contain null elements and will have a size of 2 at most. Tangential
* intersections are marked as such. line
is considered to have infinite length in both directions.
*
* @param line the Line
with which to intersect this Sphere
*
* @return an array containing all the intersections of this Sphere
and line
*
* @throws IllegalArgumentException if line
is null
*/
public final Intersection[] intersect(Line line)
{
if (line == null)
{
String message = Logging.getMessage("nullValue.LineIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
double a = line.getDirection().getLengthSquared3();
double b = 2 * line.selfDot();
double c = line.getOrigin().getLengthSquared3() - this.radius * this.radius;
double discriminant = Sphere.discriminant(a, b, c);
if (discriminant < 0)
return null;
double discriminantRoot = Math.sqrt(discriminant);
if (discriminant == 0)
{
Vec4 p = line.getPointAt((-b - discriminantRoot) / (2 * a));
return new Intersection[] {new Intersection(p, true)};
}
else // (discriminant > 0)
{
Vec4 near = line.getPointAt((-b - discriminantRoot) / (2 * a));
Vec4 far = line.getPointAt((-b + discriminantRoot) / (2 * a));
return new Intersection[] {new Intersection(near, false), new Intersection(far, false)};
}
}
/**
* Calculates a discriminant. A discriminant is useful to determine the number of roots to a quadratic equation. If
* the discriminant is less than zero, there are no roots. If it equals zero, there is one root. If it is greater
* than zero, there are two roots.
*
* @param a the coefficient of the second order pronumeral
* @param b the coefficient of the first order pronumeral
* @param c the constant parameter in the quadratic equation
*
* @return the discriminant "b squared minus 4ac"
*/
private static double discriminant(double a, double b, double c)
{
return b * b - 4 * a * c;
}
/**
* Indicates whether a specified {@link Frustum} intersects this sphere.
*
* @param frustum the frustum to test.
*
* @return true if the specified frustum intersects this sphere, otherwise false.
*
* @throws IllegalArgumentException if the frustum is null.
*/
public final boolean intersects(Frustum frustum)
{
if (frustum == null)
{
String message = Logging.getMessage("nullValue.FrustumIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// See if the extent's bounding sphere is within or intersects the frustum. The dot product of the extent's
// center point with each plane's vector provides a distance to each plane. If this distance is less than
// -radius, the extent is completely clipped by that plane and therefore does not intersect the space enclosed
// by this Frustum.
Vec4 c = this.getCenter();
double nr = -this.getRadius();
if (frustum.getFar().dot(c) <= nr)
return false;
if (frustum.getLeft().dot(c) <= nr)
return false;
if (frustum.getRight().dot(c) <= nr)
return false;
if (frustum.getTop().dot(c) <= nr)
return false;
if (frustum.getBottom().dot(c) <= nr)
return false;
//noinspection RedundantIfStatement
if (frustum.getNear().dot(c) <= nr)
return false;
return true;
}
/**
* Tests for intersection with a Line
.
*
* @param line the Line
with which to test for intersection
*
* @return true if line
intersects or makes a tangent with the surface of this Sphere
*
* @throws IllegalArgumentException if line
is null
*/
public boolean intersects(Line line)
{
if (line == null)
{
String msg = Logging.getMessage("nullValue.LineIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
return line.distanceTo(this.center) <= this.radius;
}
/**
* Tests for intersection with a Plane
.
*
* @param plane the Plane
with which to test for intersection
*
* @return true if plane
intersects or makes a tangent with the surface of this Sphere
*
* @throws IllegalArgumentException if plane
is null
*/
public boolean intersects(Plane plane)
{
if (plane == null)
{
String msg = Logging.getMessage("nullValue.PlaneIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
double dq1 = plane.dot(this.center);
return dq1 <= this.radius;
}
/** {@inheritDoc} */
public double getProjectedArea(View view)
{
if (view == null)
{
String message = Logging.getMessage("nullValue.ViewIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
return WWMath.computeSphereProjectedArea(view, this.getCenter(), this.getRadius());
}
/**
* Causes this Sphere
to render itself using the DrawContext
provided. dc
may
* not be null.
*
* @param dc the DrawContext
to be used
*
* @throws IllegalArgumentException if dc
is null
*/
public void render(DrawContext dc)
{
if (dc == null)
{
String msg = Logging.getMessage("nullValue.DrawContextIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
gl.glPushAttrib(GL2.GL_ENABLE_BIT | GL2.GL_CURRENT_BIT);
gl.glMatrixMode(GL2.GL_MODELVIEW);
gl.glPushMatrix();
gl.glTranslated(this.center.x, this.center.y, this.center.z);
GLUquadric quadric = dc.getGLU().gluNewQuadric();
dc.getGLU().gluQuadricDrawStyle(quadric, GLU.GLU_LINE);
dc.getGLU().gluSphere(quadric, this.radius, 10, 10);
gl.glPopMatrix();
dc.getGLU().gluDeleteQuadric(quadric);
gl.glPopAttrib();
}
@Override
public String toString()
{
return "Sphere: center = " + this.center.toString() + " radius = " + Double.toString(this.radius);
}
@Override
public boolean equals(Object o)
{
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
final gov.nasa.worldwind.geom.Sphere sphere = (gov.nasa.worldwind.geom.Sphere) o;
if (Double.compare(sphere.radius, radius) != 0)
return false;
//noinspection RedundantIfStatement
if (!center.equals(sphere.center))
return false;
return true;
}
@Override
public int hashCode()
{
int result;
long temp;
result = center.hashCode();
temp = radius != +0.0d ? Double.doubleToLongBits(radius) : 0L;
result = 29 * result + (int) (temp ^ (temp >>> 32));
return result;
}
// public final boolean intersects(Line line)
// {
// if (line == null)
// {
// String message = WorldWind.retrieveErrMsg("nullValue.LineIsNull");
// WorldWind.logger().logger(Level.SEVERE, message);
// throw new IllegalArgumentException(message);
// }
//
// double a = line.getDirection().getLengthSquared();
// double b = 2 * line.selfDot();
// double c = line.getOrigin().selfDot() - this.radius * this.radius;
//
// double discriminant = Sphere.discriminant(a, b, c);
// if (discriminant < 0)
// {
// return false;
// }
//
// return true;
//
// }
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy