Worldwind public release 0.2.1
[worldwind-tracker.git] / gov / nasa / worldwind / geom / Sphere.java
blob47b813b7fee80220d50af8ef7a573ce759e65e8c
1 /*
2 Copyright (C) 2001, 2006 United States Government
3 as represented by the Administrator of the
4 National Aeronautics and Space Administration.
5 All Rights Reserved.
6 */
7 package gov.nasa.worldwind.geom;
9 import gov.nasa.worldwind.*;
11 /**
12 * Represents a sphere in three dimensional space.
13 * <p/>
14 * Instances of <code>Sphere</code> are immutable. </p>
16 * @author Tom Gaskins
17 * @version $Id: Sphere.java 2126 2007-06-21 21:00:42Z dcollins $
19 public final class Sphere implements Extent, Renderable
21 public final static Sphere UNIT_SPHERE = new Sphere(Vec4.ZERO, 1);
23 private final Vec4 center;
24 private final double radius;
26 /**
27 * Creates a sphere that completely contains a set of points.
29 * @param points the <code>Vec4</code>s to be enclosed by the new Sphere
30 * @return a <code>Sphere</code> encompassing the given array of <code>Vec4</code>s
31 * @throws IllegalArgumentException if <code>points</code> is null or empty
33 public static Sphere createBoundingSphere(Vec4 points[])
35 if (points == null)
37 String message = WorldWind.retrieveErrMsg("nullValue.PointsArrayIsNull");
38 WorldWind.logger().log(java.util.logging.Level.FINE, message);
39 throw new IllegalArgumentException(message);
42 if (points.length < 1)
44 String message = WorldWind.retrieveErrMsg("geom.Sphere.NoPointsSpecified");
45 WorldWind.logger().log(java.util.logging.Level.FINE, message);
46 throw new IllegalArgumentException(message);
49 // Creates the sphere around the axis aligned bounding box of the input points.
50 Vec4[] extrema = composeExtrema(points);
51 Vec4 center = new Vec4(
52 (extrema[0].x + extrema[1].x) / 2.0,
53 (extrema[0].y + extrema[1].y) / 2.0,
54 (extrema[0].z + extrema[1].z) / 2.0);
55 double radius = extrema[0].distanceTo3(extrema[1]) / 2.0;
57 return new Sphere(center, radius);
60 /**
61 * Creates a new <code>Sphere</code> from a given center and radius. <code>radius</code> must be positive (that is,
62 * greater than zero), and <code>center</code> may not be null.
64 * @param center the center of the new sphere
65 * @param radius the radius of the new sphere
66 * @throws IllegalArgumentException if <code>center</code> is null or if <code>radius</code> is non-positive
68 public Sphere(Vec4 center, double radius)
70 if (center == null)
72 String message = WorldWind.retrieveErrMsg("nullValue.CenterIsNull");
73 WorldWind.logger().log(java.util.logging.Level.FINE, message);
74 throw new IllegalArgumentException(message);
77 if (radius <= 0)
79 String message = WorldWind.retrieveErrMsg("geom.Sphere.RadiusIsZeroOrNegative");
80 WorldWind.logger().log(java.util.logging.Level.FINE, message);
81 throw new IllegalArgumentException(message);
84 this.center = center;
85 this.radius = radius;
88 /**
89 * Calculate the extrema of a given array of <code>Vec4</code>s. The resulting array is always of length 2, with the
90 * first element containing the minimum extremum, and the second containing the maximum. The minimum extremum is
91 * composed by taking the smallest x, y and z values from all the <code>Vec4</code>s in the array. These values are
92 * not necessarily taken from the same <code>Vec4</code>. The maximum extrema is composed in the same fashion.
94 * @param points any array of <code>Vec4</code>s
95 * @return a array with length of 2, comprising the most extreme values in the given array
96 * @throws IllegalArgumentException if <code>points</code> is null
98 public static Vec4[] composeExtrema(Vec4 points[])
100 if (points == null)
102 String message = WorldWind.retrieveErrMsg("nullValue.PointsArrayIsNull");
103 WorldWind.logger().log(java.util.logging.Level.FINE, message);
104 throw new IllegalArgumentException(message);
107 if (points.length == 0)
108 return null;
110 double xmin = points[0].x;
111 double ymin = points[0].y;
112 double zmin = points[0].z;
113 double xmax = xmin;
114 double ymax = ymin;
115 double zmax = zmin;
117 for (int i = 1; i < points.length; i++)
119 double x = points[i].x;
120 if (x > xmax)
122 xmax = x;
124 else if (x < xmin)
126 xmin = x;
129 double y = points[i].y;
130 if (y > ymax)
132 ymax = y;
134 else if (y < ymin)
136 ymin = y;
139 double z = points[i].z;
140 if (z > zmax)
142 zmax = z;
144 else if (z < zmin)
146 zmin = z;
150 return new Vec4[] {new Vec4(xmin, ymin, zmin), new Vec4(xmax, ymax, zmax)};
154 * Obtains the radius of this <code>Sphere</code>. The radus is the distance from the center to the surface. If an
155 * object's distance to this sphere's center is less than or equal to the radius, then that object is at least
156 * partially within this <code>Sphere</code>.
158 * @return the radius of this sphere
160 public final double getRadius()
162 return this.radius;
166 * Obtains the diameter of this <code>Sphere</code>. The diameter is twice the radius.
168 * @return the diameter of this <code>Sphere</code>
170 public final double getDiameter()
172 return 2 * this.radius;
176 * Obtains the center of this <code>Sphere</code>.
178 * @return the <code>Vec4</code> situated at the center of this <code>Sphere</code>
180 public final Vec4 getCenter()
182 return this.center;
186 * Obtains the intersections of this sphere with a line. The returned array may be either null or of zero length if
187 * no intersections are discovered. It does not contain null elements and will have a size of 2 at most. Tangential
188 * intersections are marked as such. <code>line</code> is considered to have infinite length in both directions.
190 * @param line the <code>Line</code> with which to intersect this <code>Sphere</code>
191 * @return an array containing all the intersections of this <code>Sphere</code> and <code>line</code>
192 * @throws IllegalArgumentException if <code>line</code> is null
194 public final Intersection[] intersect(Line line)
196 if (line == null)
198 String message = WorldWind.retrieveErrMsg("nullValue.LineIsNull");
199 WorldWind.logger().log(java.util.logging.Level.FINE, message);
200 throw new IllegalArgumentException(message);
203 double a = line.getDirection().getLengthSquared3();
204 double b = 2 * line.selfDot();
205 double c = line.getOrigin().getLengthSquared3() - this.radius * this.radius;
207 double discriminant = Sphere.discriminant(a, b, c);
208 if (discriminant < 0)
209 return null;
211 double discriminantRoot = Math.sqrt(discriminant);
212 if (discriminant == 0)
214 Vec4 p = line.getPointAt((-b - discriminantRoot) / (2 * a));
215 return new Intersection[] {new Intersection(p, true)};
217 else // (discriminant > 0)
219 Vec4 near = line.getPointAt((-b - discriminantRoot) / (2 * a));
220 Vec4 far = line.getPointAt((-b + discriminantRoot) / (2 * a));
221 return new Intersection[] {new Intersection(near, false), new Intersection(far, false)};
226 * Calculates a discriminant. A discriminant is useful to determine the number of roots to a quadratic equation. If
227 * the discriminant is less than zero, there are no roots. If it equals zero, there is one root. If it is greater
228 * than zero, there are two roots.
230 * @param a the coefficient of the second order pronumeral
231 * @param b the coefficient of the first order pronumeral
232 * @param c the constant parameter in the quadratic equation
233 * @return the discriminant "b squared minus 4ac"
235 private static double discriminant(double a, double b, double c)
237 return b * b - 4 * a * c;
241 * tests for intersetion with a <code>Frustum</code>. This operation is commutative, so
242 * <code>someSphere.intersects(frustum)</code> and <code>frustum.intersects(someSphere)</code> are equivalent.
244 * @param frustum the <code>Frustum</code> with which to test for intersection
245 * @return true if either <code>frustum</code> or this <code>Sphere</code> wholly or partially contain the other,
246 * false otherwise.
247 * @throws IllegalArgumentException if <code>frustum</code> is null
249 public boolean intersects(Frustum frustum)
251 if (frustum == null)
253 String message = WorldWind.retrieveErrMsg("nullValue.FrustumIsNull");
254 WorldWind.logger().log(java.util.logging.Level.FINE, message);
255 throw new IllegalArgumentException(message);
258 return frustum.intersects(this);
262 * Tests for intersection with a <code>Line</code>.
264 * @param line the <code>Line</code> with which to test for intersection
265 * @return true if <code>line</code> intersects or makes a tangent with the surface of this <code>Sphere</code>
266 * @throws IllegalArgumentException if <code>line</code> is null
268 public boolean intersects(Line line)
270 if (line == null)
272 String msg = WorldWind.retrieveErrMsg("nullValue.LineIsNull");
273 WorldWind.logger().log(java.util.logging.Level.FINE, msg);
274 throw new IllegalArgumentException(msg);
276 return line.distanceTo(this.center) <= this.radius;
280 * Tests for intersection with a <code>Plane</code>.
282 * @param plane the <code>Plane</code> with which to test for intersection
283 * @return true if <code>plane</code> intersects or makes a tangent with the surface of this <code>Sphere</code>
284 * @throws IllegalArgumentException if <code>plane</code> is null
286 public boolean intersects(Plane plane)
288 if (plane == null)
290 String msg = WorldWind.retrieveErrMsg("nullValue.PlaneIsNull");
291 WorldWind.logger().log(java.util.logging.Level.FINE, msg);
292 throw new IllegalArgumentException(msg);
295 double dq1 = plane.dot(this.center);
296 return dq1 <= this.radius;
300 * Causes this <code>Sphere</code> to render itself using the <code>DrawContext</code> provided. <code>dc</code> may
301 * not be null.
303 * @param dc the <code>DrawContext</code> to be used
304 * @throws IllegalArgumentException if <code>dc</code> is null
306 public void render(gov.nasa.worldwind.DrawContext dc)
308 if (dc == null)
310 String msg = WorldWind.retrieveErrMsg("nullValue.DrawContextIsNull");
311 WorldWind.logger().log(java.util.logging.Level.FINE, msg);
312 throw new IllegalArgumentException(msg);
315 javax.media.opengl.GL gl = dc.getGL();
317 gl.glPushAttrib(javax.media.opengl.GL.GL_TEXTURE_BIT | javax.media.opengl.GL.GL_ENABLE_BIT
318 | javax.media.opengl.GL.GL_CURRENT_BIT);
319 gl.glDisable(javax.media.opengl.GL.GL_TEXTURE_2D);
320 gl.glColor3d(1, 1, 0);
322 gl.glMatrixMode(javax.media.opengl.GL.GL_MODELVIEW);
323 gl.glPushMatrix();
324 gl.glTranslated(this.center.x, this.center.y, this.center.z);
325 javax.media.opengl.glu.GLUquadric quadric = dc.getGLU().gluNewQuadric();
326 dc.getGLU().gluQuadricDrawStyle(quadric, javax.media.opengl.glu.GLU.GLU_LINE);
327 dc.getGLU().gluSphere(quadric, this.radius, 10, 10);
328 gl.glPopMatrix();
329 dc.getGLU().gluDeleteQuadric(quadric);
331 gl.glPopAttrib();
334 @Override
335 public String toString()
337 return "Sphere: center = " + this.center.toString() + " radius = " + Double.toString(this.radius);
340 @Override
341 public boolean equals(Object o)
343 if (this == o)
344 return true;
345 if (o == null || getClass() != o.getClass())
346 return false;
348 final gov.nasa.worldwind.geom.Sphere sphere = (gov.nasa.worldwind.geom.Sphere) o;
350 if (Double.compare(sphere.radius, radius) != 0)
351 return false;
352 if (!center.equals(sphere.center))
353 return false;
355 return true;
358 @Override
359 public int hashCode()
361 int result;
362 long temp;
363 result = center.hashCode();
364 temp = radius != +0.0d ? Double.doubleToLongBits(radius) : 0L;
365 result = 29 * result + (int) (temp ^ (temp >>> 32));
366 return result;
369 // public final boolean intersects(Line line)
370 // {
371 // if (line == null)
372 // {
373 // String message = WorldWind.retrieveErrMsg("nullValue.LineIsNull");
374 // WorldWind.logger().log(java.util.logging.Level.FINE, message);
375 // throw new IllegalArgumentException(message);
376 // }
378 // double a = line.getDirection().getLengthSquared();
379 // double b = 2 * line.selfDot();
380 // double c = line.getOrigin().selfDot() - this.radius * this.radius;
382 // double discriminant = Sphere.discriminant(a, b, c);
383 // if (discriminant < 0)
384 // {
385 // return false;
386 // }
388 // return true;
390 // }