Update to Worldwind release 0.4.1
[worldwind-tracker.git] / gov / nasa / worldwind / geom / Frustum.java
blob93237c527612571eeee6caedb73ac2bae374dfef
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.util.Logging;
11 //todo: check javadoc accuracy,
13 /**
14 * Instances of <code>Frustum</code> are immutable. </p>
16 * @author Tom Gaskins
17 * @version $Id: Frustum.java 3301 2007-10-16 00:31:34Z dcollins $
19 public class Frustum
21 private final Plane left;
22 private final Plane right;
23 private final Plane bottom;
24 private final Plane top;
25 private final Plane near;
26 private final Plane far;
27 private final Plane[] allPlanes;
29 /**
30 * Create a default frustum with six <code>Plane</code>s. This defines a box of dimension (2, 2, 2) centered at the
31 * origin.
33 public Frustum()
35 this(
36 new Plane(1, 0, 0, 1), // Left
37 new Plane(-1, 0, 0, 1), // Right
38 new Plane(0, 1, 0, 1), // Bottom
39 new Plane(0, -1, 0, 1), // Top
40 new Plane(0, 0, -1, 1), // Near
41 new Plane(0, 0, 1, 1)); // Far
44 /**
45 * Create a frustum from six <code>Plane</code>s, which define its boundaries. Does not except null arguments.
47 * @param near the near plane
48 * @param far the far plane
49 * @param left the left side of the view frustum
50 * @param right the right side of the view frustm
51 * @param top the top of the view frustum
52 * @param bottom the bottom of the view frustum
53 * @throws IllegalArgumentException if any argument is null
55 public Frustum(Plane left, Plane right, Plane bottom, Plane top, Plane near, Plane far)
57 if (left == null || right == null || bottom == null || top == null || near == null || far == null)
59 String message = Logging.getMessage("nullValue.PlaneIsNull");
60 Logging.logger().severe(message);
61 throw new IllegalArgumentException(message);
63 this.left = left;
64 this.right = right;
65 this.bottom = bottom;
66 this.top = top;
67 this.near = near;
68 this.far = far;
70 this.allPlanes = new Plane[] {this.left, this.right, this.bottom, this.top, this.near, this.far};
73 public boolean equals(Object obj)
75 if (this == obj)
76 return true;
77 if (obj == null || getClass() != obj.getClass())
78 return false;
80 Frustum that = (Frustum) obj;
81 return this.left.equals(that.left)
82 && this.right.equals(that.right)
83 && this.bottom.equals(that.bottom)
84 && this.top.equals(that.top)
85 && this.near.equals(that.near)
86 && this.far.equals(that.far);
89 public final Plane getLeft()
91 return this.left;
94 public final Plane getRight()
96 return this.right;
99 public final Plane getBottom()
101 return this.bottom;
104 public final Plane getTop()
106 return this.top;
109 public final Plane getNear()
111 return this.near;
114 public final Plane getFar()
116 return this.far;
119 public Plane[] getAllPlanes()
121 return this.allPlanes;
124 public int hashCode()
126 int result;
127 result = this.left.hashCode();
128 result = 31 * result + this.right.hashCode();
129 result = 19 * result + this.bottom.hashCode();
130 result = 23 * result + this.top.hashCode();
131 result = 17 * result + this.near.hashCode();
132 result = 19 * result + this.far.hashCode();
134 return result;
137 public String toString()
139 StringBuilder sb = new StringBuilder();
140 sb.append("(");
141 sb.append("left=").append(this.near);
142 sb.append(", right=").append(this.near);
143 sb.append(", bottom=").append(this.near);
144 sb.append(", top=").append(this.near);
145 sb.append(", near=").append(this.near);
146 sb.append(", far=").append(this.near);
147 sb.append(")");
148 return sb.toString();
151 // ============== Factory Functions ======================= //
152 // ============== Factory Functions ======================= //
153 // ============== Factory Functions ======================= //
155 public static Frustum fromProjectionMatrix(Matrix projectionMatrix)
157 //noinspection UnnecessaryLocalVariable
158 Matrix m = projectionMatrix;
159 if (m == null)
161 String message = Logging.getMessage("nullValue.MatrixIsNull");
162 Logging.logger().severe(message);
163 throw new IllegalArgumentException(message);
166 // Extract the left clipping plane from the projection-matrix.
167 double leftMag = Math.sqrt((m.m41 + m.m11) * (m.m41 + m.m11) + (m.m42 + m.m12) * (m.m42 + m.m12)
168 + (m.m43 + m.m13) * (m.m43 + m.m13));
169 Plane leftPlane = new Plane((m.m41 + m.m11) / leftMag, (m.m42 + m.m12) / leftMag, (m.m43 + m.m13) / leftMag,
170 m.m44 + m.m14);
171 // Extract the right clipping plane from the projection-matrix.
172 double rightMag = Math.sqrt((m.m41 - m.m11) * (m.m41 - m.m11) + (m.m42 - m.m12) * (m.m42 - m.m12)
173 + (m.m43 - m.m13) * (m.m43 - m.m13));
174 Plane rightPlane = new Plane((m.m41 - m.m11) / rightMag, (m.m42 - m.m12) / rightMag, (m.m43 - m.m13) / rightMag,
175 m.m44 - m.m14);
176 // Extract the bottom clipping plane from the projection-matrix.
177 double bottomMag = Math.sqrt((m.m41 + m.m21) * (m.m41 + m.m21) + (m.m42 + m.m22) * (m.m42 + m.m22)
178 + (m.m43 + m.m23) * (m.m43 + m.m23));
179 Plane bottomPlane = new Plane((m.m41 + m.m21) / bottomMag, (m.m42 + m.m22) / bottomMag,
180 (m.m43 + m.m23) / bottomMag,
181 m.m44 + m.m24);
182 // Extract the top clipping plane from the projection-matrix.
183 double topMag = Math.sqrt((m.m41 - m.m21) * (m.m41 - m.m21) + (m.m42 - m.m22) * (m.m42 - m.m22)
184 + (m.m43 - m.m23) * (m.m43 - m.m23));
185 Plane topPlane = new Plane((m.m41 - m.m21) / topMag, (m.m42 - m.m22) / topMag, (m.m43 - m.m23) / topMag,
186 m.m44 - m.m24);
187 // Extract the near clipping plane from the projection-matrix.
188 double nearMag = Math.sqrt((m.m41 + m.m31) * (m.m41 + m.m31) + (m.m42 + m.m32) * (m.m42 + m.m32)
189 + (m.m43 + m.m33) * (m.m43 + m.m33));
190 Plane nearPlane = new Plane((m.m41 + m.m31) / nearMag, (m.m42 + m.m32) / nearMag, (m.m43 + m.m33) / nearMag,
191 m.m44 + m.m34);
192 // Extract the far clipping plane from the projection-matrix.
193 double farMag = Math.sqrt((m.m41 - m.m31) * (m.m41 - m.m31) + (m.m42 - m.m32) * (m.m42 - m.m32)
194 + (m.m43 - m.m33) * (m.m43 - m.m33));
195 Plane farPlane = new Plane((m.m41 - m.m31) / farMag, (m.m42 - m.m32) / farMag, (m.m43 - m.m33) / farMag,
196 m.m44 - m.m34);
197 return new Frustum(leftPlane, rightPlane, bottomPlane, topPlane, nearPlane, farPlane);
201 * Creates a <code>Frustum</code> from a horizontal field-of-view, viewport aspect ratio and distance to near and
202 * far depth clipping planes. The near plane must be closer than the far plane, and both planes must be a positive
203 * distance away.
205 * @param horizontalFieldOfView horizontal field-of-view angle in the range (0, 180)
206 * @param viewportWidth the width of the viewport in screen pixels
207 * @param viewportHeight the height of the viewport in screen pixels
208 * @param near distance to the near depth clipping plane
209 * @param far distance to far depth clipping plane
210 * @throws IllegalArgumentException if fov is not in the range (0, 180), if either near or far are negative, or near
211 * is greater than or equal to far
213 public static Frustum fromPerspective(Angle horizontalFieldOfView, int viewportWidth, int viewportHeight,
214 double near, double far)
216 if (horizontalFieldOfView == null)
218 String message = Logging.getMessage("Geom.ViewFrustum.FieldOfViewIsNull");
219 Logging.logger().fine(message);
220 throw new IllegalArgumentException(message);
222 double fov = horizontalFieldOfView.getDegrees();
223 double farMinusNear = far - near;
224 String message = null;
225 if (fov <= 0 || fov > 180)
226 message = Logging.getMessage("Geom.ViewFrustum.FieldOfViewOutOfRange", fov);
227 if (near <= 0 || farMinusNear <= 0)
228 message = Logging.getMessage("Geom.ViewFrusutm.ClippingDistanceOutOfRange");
229 if (message != null)
231 Logging.logger().severe(message);
232 throw new IllegalArgumentException(message);
235 double focalLength = 1d / horizontalFieldOfView.tanHalfAngle();
236 double aspect = viewportHeight / (double) viewportWidth;
237 double lrLen = Math.sqrt(focalLength * focalLength + 1);
238 double btLen = Math.sqrt(focalLength * focalLength + aspect * aspect);
239 Plane leftPlane = new Plane(focalLength / lrLen, 0d, 0d - 1d / lrLen, 0);
240 Plane rightPlane = new Plane(0d - focalLength / lrLen, 0d, 0d - 1d / lrLen, 0d);
241 Plane bottomPlane = new Plane(0d, focalLength / btLen, 0d - aspect / btLen, 0d);
242 Plane topPlane = new Plane(0d, 0d - focalLength / btLen, 0d - aspect / btLen, 0d);
243 Plane nearPlane = new Plane(0d, 0d, 0d - 1d, 0d - near);
244 Plane farPlane = new Plane(0d, 0d, 1d, far);
245 return new Frustum(leftPlane, rightPlane, bottomPlane, topPlane, nearPlane, farPlane);
249 * Creates a <code>Frustum</code> from three sets of parallel clipping planes (a parallel projectionMatrix). In this
250 * case, the near and far depth clipping planes may be a negative distance away.
252 * @param near distance to the near depth clipping plane
253 * @param far distance to far depth clipping plane
254 * @param width horizontal dimension of the near clipping plane
255 * @param height vertical dimension of the near clipping plane
256 * @throws IllegalArgumentException if the difference of any plane set (lright - left, top - bottom, far - near) is
257 * less than or equal to zero.
259 public static Frustum fromPerspective(double width, double height, double near, double far)
261 double farMinusNear = far - near;
262 if (farMinusNear <= 0.0 || width <= 0.0 || height <= 0.0)
264 String message = Logging.getMessage("Geom.ViewFrusutm.ClippingDistanceOutOfRange");
265 Logging.logger().fine(message);
266 throw new IllegalArgumentException(message);
269 double width_over_2 = width / 2.0;
270 double height_over_2 = height / 2.0;
271 Plane leftPlane = new Plane(1.0, 0.0, 0.0, width_over_2);
272 Plane rightPlane = new Plane(-1.0, 0.0, 0.0, width_over_2);
273 Plane bottomPlane = new Plane(0.0, 1.0, 0.0, height_over_2);
274 Plane topPlane = new Plane(0.0, -1.0, 0.0, height_over_2);
275 Plane nearPlane = new Plane(0.0, 0.0, -1.0, (near < 0.0) ? near : -near);
276 Plane farPlane = new Plane(0.0, 0.0, 1.0, (far < 0.0) ? -far : far);
277 return new Frustum(leftPlane, rightPlane, bottomPlane, topPlane, nearPlane, farPlane);
280 // ============== Intersection Functions ======================= //
281 // ============== Intersection Functions ======================= //
282 // ============== Intersection Functions ======================= //
285 * @param extent
286 * @return
287 * @throws IllegalArgumentException if <code>extent</code> is null
289 public final boolean intersects(Extent extent)
291 if (extent == null)
293 String msg = Logging.getMessage("nullValue.ExtentIsNull");
294 Logging.logger().fine(msg);
295 throw new IllegalArgumentException(msg);
298 // See if the extent's bounding sphere is within or intersects the frustum.
299 Vec4 c = extent.getCenter();
300 double nr = -extent.getRadius();
302 if (this.far.dot(c) <= nr)
303 return false;
304 if (this.left.dot(c) <= nr)
305 return false;
306 if (this.right.dot(c) <= nr)
307 return false;
308 if (this.top.dot(c) <= nr)
309 return false;
310 if (this.bottom.dot(c) <= nr)
311 return false;
312 //noinspection RedundantIfStatement
313 if (this.near.dot(c) <= nr)
314 return false;
316 return true;
320 * @param point
321 * @return
322 * @throws IllegalArgumentException if <code>point</code> is null
324 public final boolean contains(Vec4 point)
326 if (point == null)
328 String msg = Logging.getMessage("nullValue.PointIsNull");
329 Logging.logger().fine(msg);
330 throw new IllegalArgumentException(msg);
333 if (this.far.dot(point) <= 0)
334 return false;
335 if (this.left.dot(point) <= 0)
336 return false;
337 if (this.right.dot(point) <= 0)
338 return false;
339 if (this.top.dot(point) <= 0)
340 return false;
341 if (this.bottom.dot(point) <= 0)
342 return false;
343 //noinspection RedundantIfStatement
344 if (this.near.dot(point) <= 0)
345 return false;
347 return true;
350 // ============== Geometric Functions ======================= //
351 // ============== Geometric Functions ======================= //
352 // ============== Geometric Functions ======================= //
355 * @param matrix
356 * @return
357 * @throws IllegalArgumentException if <code>matrix</code> is null
359 public Frustum transformBy(Matrix matrix)
361 ensureNonNull(matrix);
362 Plane left = new Plane(this.left.getVector().transformBy4(matrix));
363 Plane right = new Plane(this.right.getVector().transformBy4(matrix));
364 Plane bottom = new Plane(this.bottom.getVector().transformBy4(matrix));
365 Plane top = new Plane(this.top.getVector().transformBy4(matrix));
366 Plane near = new Plane(this.near.getVector().transformBy4(matrix));
367 Plane far = new Plane(this.far.getVector().transformBy4(matrix));
368 return new Frustum(left, right, bottom, top, near, far);
371 // ============== Helper Functions ======================= //
372 // ============== Helper Functions ======================= //
373 // ============== Helper Functions ======================= //
375 private static void ensureNonNull(Matrix matrix)
377 if (matrix == null)
379 String msg = Logging.getMessage("nullValue.MatrixIsNull");
380 Logging.logger().fine(msg);
381 throw new IllegalArgumentException(msg);