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

src.gov.nasa.worldwind.geom.Sphere Maven / Gradle / Ivy

Go to download

World Wind is a collection of components that interactively display 3D geographic information within Java applications or applets.

There is a newer version: 2.0.0-986
Show 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 javax.media.opengl.GL2;
import javax.media.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 Vec4s to be enclosed by the new Sphere * * @return a Sphere encompassing the given array of Vec4s * * @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 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