2 Copyright (C) 2001, 2006 United States Government
3 as represented by the Administrator of the
4 National Aeronautics and Space Administration.
7 package gov
.nasa
.worldwind
.geom
;
9 import gov
.nasa
.worldwind
.*;
12 * Represents a sphere in three dimensional space.
14 * Instances of <code>Sphere</code> are immutable. </p>
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
;
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
[])
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
);
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
)
72 String message
= WorldWind
.retrieveErrMsg("nullValue.CenterIsNull");
73 WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, message
);
74 throw new IllegalArgumentException(message
);
79 String message
= WorldWind
.retrieveErrMsg("geom.Sphere.RadiusIsZeroOrNegative");
80 WorldWind
.logger().log(java
.util
.logging
.Level
.FINE
, message
);
81 throw new IllegalArgumentException(message
);
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
[])
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)
110 double xmin
= points
[0].x
;
111 double ymin
= points
[0].y
;
112 double zmin
= points
[0].z
;
117 for (int i
= 1; i
< points
.length
; i
++)
119 double x
= points
[i
].x
;
129 double y
= points
[i
].y
;
139 double z
= points
[i
].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()
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()
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
)
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)
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,
247 * @throws IllegalArgumentException if <code>frustum</code> is null
249 public boolean intersects(Frustum frustum
)
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
)
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
)
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
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
)
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
);
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);
329 dc
.getGLU().gluDeleteQuadric(quadric
);
335 public String
toString()
337 return "Sphere: center = " + this.center
.toString() + " radius = " + Double
.toString(this.radius
);
341 public boolean equals(Object o
)
345 if (o
== null || getClass() != o
.getClass())
348 final gov
.nasa
.worldwind
.geom
.Sphere sphere
= (gov
.nasa
.worldwind
.geom
.Sphere
) o
;
350 if (Double
.compare(sphere
.radius
, radius
) != 0)
352 if (!center
.equals(sphere
.center
))
359 public int hashCode()
363 result
= center
.hashCode();
364 temp
= radius
!= +0.0d ? Double
.doubleToLongBits(radius
) : 0L;
365 result
= 29 * result
+ (int) (temp ^
(temp
>>> 32));
369 // public final boolean intersects(Line line)
373 // String message = WorldWind.retrieveErrMsg("nullValue.LineIsNull");
374 // WorldWind.logger().log(java.util.logging.Level.FINE, message);
375 // throw new IllegalArgumentException(message);
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)