Updated to worldwind release 20070817
[worldwind-tracker.git] / gov / nasa / worldwind / geom / Frustum.java
blobac535dba2ac665bf2ea35d8f21af920dbe5aaa49
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 2471 2007-07-31 21:50:57Z tgaskins $
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;
28 /**
29 * Create a default frustum with six <code>Plane</code>s. This defines a box of dimension (2, 2, 2) centered at the
30 * origin.
32 public Frustum()
34 this(
35 new Plane(1, 0, 0, 1), // Left
36 new Plane(-1, 0, 0, 1), // Right
37 new Plane(0, 1, 0, 1), // Bottom
38 new Plane(0, -1, 0, 1), // Top
39 new Plane(0, 0, 1, 1), // Near
40 new Plane(0, 0, -1, 1)); // Far
43 /**
44 * Create a frustum from six <code>Plane</code>s, which define its boundaries. Does not except null arguments.
46 * @param near the near plane
47 * @param far the far plane
48 * @param left the left side of the view frustum
49 * @param right the right side of the view frustm
50 * @param top the top of the view frustum
51 * @param bottom the bottom of the view frustum
52 * @throws IllegalArgumentException if any argument is null
54 public Frustum(Plane left, Plane right, Plane bottom, Plane top, Plane near, Plane far)
56 if (left == null || right == null || bottom == null || top == null || near == null || far == null)
58 String message = Logging.getMessage("nullValue.PlaneIsNull");
59 Logging.logger().severe(message);
60 throw new IllegalArgumentException(message);
62 this.left = left;
63 this.right = right;
64 this.bottom = bottom;
65 this.top = top;
66 this.near = near;
67 this.far = far;
70 public boolean equals(Object obj)
72 if (this == obj)
73 return true;
74 if (obj == null || getClass() != obj.getClass())
75 return false;
77 Frustum that = (Frustum) obj;
78 return this.left.equals(that.left)
79 && this.right.equals(that.right)
80 && this.bottom.equals(that.bottom)
81 && this.top.equals(that.top)
82 && this.near.equals(that.near)
83 && this.far.equals(that.far);
86 public final Plane getLeft()
88 return this.left;
91 public final Plane getRight()
93 return this.right;
96 public final Plane getBottom()
98 return this.bottom;
101 public final Plane getTop()
103 return this.top;
106 public final Plane getNear()
108 return this.near;
111 public final Plane getFar()
113 return this.far;
116 public int hashCode()
118 int result;
119 result = this.left.hashCode();
120 result = 31 * result + this.right.hashCode();
121 result = 19 * result + this.bottom.hashCode();
122 result = 23 * result + this.top.hashCode();
123 result = 17 * result + this.near.hashCode();
124 result = 19 * result + this.far.hashCode();
126 return result;
129 public String toString()
131 StringBuilder sb = new StringBuilder();
132 sb.append("(");
133 sb.append("left=").append(this.near);
134 sb.append(", right=").append(this.near);
135 sb.append(", bottom=").append(this.near);
136 sb.append(", top=").append(this.near);
137 sb.append(", near=").append(this.near);
138 sb.append(", far=").append(this.near);
139 sb.append(")");
140 return sb.toString();
143 // ============== Factory Functions ======================= //
144 // ============== Factory Functions ======================= //
145 // ============== Factory Functions ======================= //
147 public static Frustum fromProjectionMatrix(Matrix projectionMatrix)
149 //noinspection UnnecessaryLocalVariable
150 Matrix m = projectionMatrix;
151 if (m == null)
153 String message = Logging.getMessage("nullValue.MatrixIsNull");
154 Logging.logger().severe(message);
155 throw new IllegalArgumentException(message);
158 // Extract the left clipping plane from the projectionMatrix-matrix.
159 double leftMag = Math.sqrt((m.m41 + m.m11) * (m.m41 + m.m11) + (m.m42 + m.m12) * (m.m42 + m.m12)
160 + (m.m43 + m.m13) * (m.m43 + m.m13));
161 Plane leftPlane = new Plane((m.m41 + m.m11) / leftMag, (m.m42 + m.m12) / leftMag, (m.m43 + m.m13) / leftMag,
162 m.m44 + m.m14);
163 // Extract the right clipping plane from the projectionMatrix-matrix.
164 double rightMag = Math.sqrt((m.m41 - m.m11) * (m.m41 - m.m11) + (m.m42 - m.m12) * (m.m42 - m.m12)
165 + (m.m43 - m.m13) * (m.m43 - m.m13));
166 Plane rightPlane = new Plane((m.m41 - m.m11) / rightMag, (m.m42 - m.m12) / rightMag, (m.m43 - m.m13) / rightMag,
167 m.m44 - m.m14);
168 // Extract the bottom clipping plane from the projectionMatrix-matrix.
169 double bottomMag = Math.sqrt((m.m41 + m.m21) * (m.m41 + m.m21) + (m.m42 + m.m22) * (m.m42 + m.m22)
170 + (m.m43 + m.m23) * (m.m43 + m.m23));
171 Plane bottomPlane = new Plane((m.m41 + m.m21) / bottomMag, (m.m42 + m.m22) / bottomMag,
172 (m.m43 + m.m23) / bottomMag,
173 m.m44 + m.m24);
174 // Extract the top clipping plane from the projectionMatrix-matrix.
175 double topMag = Math.sqrt((m.m41 - m.m21) * (m.m41 - m.m21) + (m.m42 - m.m22) * (m.m42 - m.m22)
176 + (m.m43 - m.m23) * (m.m43 - m.m23));
177 Plane topPlane = new Plane((m.m41 - m.m21) / topMag, (m.m42 - m.m22) / topMag, (m.m43 - m.m23) / topMag,
178 m.m44 - m.m24);
179 // Extract the near clipping plane from the projectionMatrix-matrix.
180 double nearMag = Math.sqrt((m.m41 + m.m31) * (m.m41 + m.m31) + (m.m42 + m.m32) * (m.m42 + m.m32)
181 + (m.m43 + m.m33) * (m.m43 + m.m33));
182 Plane nearPlane = new Plane((m.m41 + m.m31) / nearMag, (m.m42 + m.m32) / nearMag, (m.m43 + m.m33) / nearMag,
183 m.m44 + m.m34);
184 // Extract the far clipping plane from the projectionMatrix-matrix.
185 double farMag = Math.sqrt((m.m41 - m.m31) * (m.m41 - m.m31) + (m.m42 - m.m32) * (m.m42 - m.m32)
186 + (m.m43 - m.m33) * (m.m43 - m.m33));
187 Plane farPlane = new Plane((m.m41 - m.m31) / farMag, (m.m42 - m.m32) / farMag, (m.m43 - m.m33) / farMag,
188 m.m44 - m.m34);
189 return new Frustum(leftPlane, rightPlane, bottomPlane, topPlane, nearPlane, farPlane);
193 * Creates a <code>Frustum</code> from a horizontal field-of-view, viewport aspect ratio and distance to near and
194 * far depth clipping planes. The near plane must be closer than the far plane, and both planes must be a positive
195 * distance away.
197 * @param horizontalFieldOfView horizontal field-of-view angle in the range (0, 180)
198 * @param viewportWidth the width of the viewport in screen pixels
199 * @param viewportHeight the height of the viewport in screen pixels
200 * @param near distance to the near depth clipping plane
201 * @param far distance to far depth clipping plane
202 * @throws IllegalArgumentException if fov is not in the range (0, 180), if either near or far are negative, or near
203 * is greater than or equal to far
205 public static Frustum fromPerspective(Angle horizontalFieldOfView, int viewportWidth, int viewportHeight,
206 double near, double far)
208 if (horizontalFieldOfView == null)
210 String message = Logging.getMessage("Geom.ViewFrustum.FieldOfViewIsNull");
211 Logging.logger().fine(message);
212 throw new IllegalArgumentException(message);
214 double fov = horizontalFieldOfView.getDegrees();
215 double farMinusNear = far - near;
216 String message = null;
217 if (fov <= 0 || fov > 180)
218 message = Logging.getMessage("Geom.ViewFrustum.FieldOfViewOutOfRange", fov);
219 if (near <= 0 || farMinusNear <= 0)
220 message = Logging.getMessage("Geom.ViewFrusutm.ClippingDistanceOutOfRange");
221 if (message != null)
223 Logging.logger().severe(message);
224 throw new IllegalArgumentException(message);
227 double focalLength = 1d / horizontalFieldOfView.tanHalfAngle();
228 double aspect = viewportHeight / (double) viewportWidth;
229 double lrLen = Math.sqrt(focalLength * focalLength + 1);
230 double btLen = Math.sqrt(focalLength * focalLength + aspect * aspect);
231 Plane leftPlane = new Plane(focalLength / lrLen, 0d, 0d - 1d / lrLen, 0);
232 Plane rightPlane = new Plane(0d - focalLength / lrLen, 0d, 0d - 1d / lrLen, 0d);
233 Plane bottomPlane = new Plane(0d, focalLength / btLen, 0d - aspect / btLen, 0d);
234 Plane topPlane = new Plane(0d, 0d - focalLength / btLen, 0d - aspect / btLen, 0d);
235 Plane nearPlane = new Plane(0d, 0d, 0d - 1d, 0d - near);
236 Plane farPlane = new Plane(0d, 0d, 1d, far);
237 return new Frustum(leftPlane, rightPlane, bottomPlane, topPlane, nearPlane, farPlane);
241 * Creates a <code>Frustum</code> from three sets of parallel clipping planes (a parallel projectionMatrix). In this
242 * case, the near and far depth clipping planes may be a negative distance away.
244 * @param near distance to the near depth clipping plane
245 * @param far distance to far depth clipping plane
246 * @param width horizontal dimension of the near clipping plane
247 * @param height vertical dimension of the near clipping plane
248 * @throws IllegalArgumentException if the difference of any plane set (lright - left, top - bottom, far - near) is
249 * less than or equal to zero.
251 public static Frustum fromPerspective(double width, double height, double near, double far)
253 double farMinusNear = far - near;
254 if (farMinusNear <= 0.0 || width <= 0.0 || height <= 0.0)
256 String message = Logging.getMessage("Geom.ViewFrusutm.ClippingDistanceOutOfRange");
257 Logging.logger().fine(message);
258 throw new IllegalArgumentException(message);
261 double width_over_2 = width / 2.0;
262 double height_over_2 = height / 2.0;
263 Plane leftPlane = new Plane(1.0, 0.0, 0.0, width_over_2);
264 Plane rightPlane = new Plane(-1.0, 0.0, 0.0, width_over_2);
265 Plane bottomPlane = new Plane(0.0, 1.0, 0.0, height_over_2);
266 Plane topPlane = new Plane(0.0, -1.0, 0.0, height_over_2);
267 Plane nearPlane = new Plane(0.0, 0.0, -1.0, (near < 0.0) ? near : -near);
268 Plane farPlane = new Plane(0.0, 0.0, 1.0, (far < 0.0) ? -far : far);
269 return new Frustum(leftPlane, rightPlane, bottomPlane, topPlane, nearPlane, farPlane);
272 // ============== Intersection Functions ======================= //
273 // ============== Intersection Functions ======================= //
274 // ============== Intersection Functions ======================= //
277 * @param extent
278 * @return
279 * @throws IllegalArgumentException if <code>extent</code> is null
281 public final boolean intersects(Extent extent)
283 if (extent == null)
285 String msg = Logging.getMessage("nullValue.ExtentIsNull");
286 Logging.logger().fine(msg);
287 throw new IllegalArgumentException(msg);
290 // See if the extent's bounding sphere is within or intersects the frustum.
291 Vec4 c = extent.getCenter();
292 double nr = -extent.getRadius();
294 if (this.far.dot(c) <= nr)
295 return false;
296 if (this.left.dot(c) <= nr)
297 return false;
298 if (this.right.dot(c) <= nr)
299 return false;
300 if (this.top.dot(c) <= nr)
301 return false;
302 if (this.bottom.dot(c) <= nr)
303 return false;
304 //noinspection RedundantIfStatement
305 if (this.near.dot(c) <= nr)
306 return false;
308 return true;
312 * @param point
313 * @return
314 * @throws IllegalArgumentException if <code>point</code> is null
316 public final boolean contains(Vec4 point)
318 if (point == null)
320 String msg = Logging.getMessage("nullValue.PointIsNull");
321 Logging.logger().fine(msg);
322 throw new IllegalArgumentException(msg);
325 if (this.far.dot(point) < 0)
326 return false;
327 if (this.left.dot(point) < 0)
328 return false;
329 if (this.right.dot(point) < 0)
330 return false;
331 if (this.top.dot(point) < 0)
332 return false;
333 if (this.bottom.dot(point) < 0)
334 return false;
335 //noinspection RedundantIfStatement
336 if (this.near.dot(point) < 0)
337 return false;
339 return true;
342 // ============== Geometric Functions ======================= //
343 // ============== Geometric Functions ======================= //
344 // ============== Geometric Functions ======================= //
347 * @param matrix
348 * @return
349 * @throws IllegalArgumentException if <code>matrix</code> is null
351 public Frustum transformBy(Matrix matrix)
353 ensureNonNull(matrix);
354 Plane left = new Plane(this.left.getVector().transformBy4(matrix));
355 Plane right = new Plane(this.right.getVector().transformBy4(matrix));
356 Plane bottom = new Plane(this.bottom.getVector().transformBy4(matrix));
357 Plane top = new Plane(this.top.getVector().transformBy4(matrix));
358 Plane near = new Plane(this.near.getVector().transformBy4(matrix));
359 Plane far = new Plane(this.far.getVector().transformBy4(matrix));
360 return new Frustum(left, right, bottom, top, near, far);
363 // ============== Helper Functions ======================= //
364 // ============== Helper Functions ======================= //
365 // ============== Helper Functions ======================= //
367 private static void ensureNonNull(Matrix matrix)
369 if (matrix == null)
371 String msg = Logging.getMessage("nullValue.MatrixIsNull");
372 Logging.logger().fine(msg);
373 throw new IllegalArgumentException(msg);