Worldwind public release 0.2.1
[worldwind-tracker.git] / gov / nasa / worldwind / geom / Frustum.java
blob0b1cbac70223c76ccc6ab06340f13b241f2fab8e
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 import java.util.logging.Level;
13 //todo: check javadoc accuracy,
15 /**
16 * Instances of <code>Frustum</code> are immutable. </p>
18 * @author Tom Gaskins
19 * @version $Id: Frustum.java 1994 2007-06-11 16:33:33Z dcollins $
21 public class Frustum
23 private final Plane left;
24 private final Plane right;
25 private final Plane bottom;
26 private final Plane top;
27 private final Plane near;
28 private final Plane far;
30 /**
31 * Create a default frustum with six <code>Plane</code>s. This defines a box of dimension (2, 2, 2) centered at the
32 * origin.
34 public Frustum()
36 this(
37 new Plane(1, 0, 0, 1), // Left
38 new Plane(-1, 0, 0, 1), // Right
39 new Plane(0, 1, 0, 1), // Bottom
40 new Plane(0, -1, 0, 1), // Top
41 new Plane(0, 0, 1, 1), // Near
42 new Plane(0, 0, -1, 1)); // Far
45 /**
46 * Create a frustum from six <code>Plane</code>s, which define its boundaries. Does not except null arguments.
48 * @param near the near plane
49 * @param far the far plane
50 * @param left the left side of the view frustum
51 * @param right the right side of the view frustm
52 * @param top the top of the view frustum
53 * @param bottom the bottom of the view frustum
54 * @throws IllegalArgumentException if any argument is null
56 public Frustum(Plane left, Plane right, Plane bottom, Plane top, Plane near, Plane far)
58 if (left == null || right == null || bottom == null || top == null || near == null || far == null)
60 String message = WorldWind.retrieveErrMsg("nullValue.PlaneIsNull");
61 WorldWind.logger().log(java.util.logging.Level.FINE, message);
62 throw new IllegalArgumentException(message);
64 this.left = left;
65 this.right = right;
66 this.bottom = bottom;
67 this.top = top;
68 this.near = near;
69 this.far = far;
72 public boolean equals(Object obj)
74 if (this == obj)
75 return true;
76 if (obj == null || getClass() != obj.getClass())
77 return false;
79 Frustum that = (Frustum) obj;
80 return this.left.equals(that.left)
81 && this.right.equals(that.right)
82 && this.bottom.equals(that.bottom)
83 && this.top.equals(that.top)
84 && this.near.equals(that.near)
85 && this.far.equals(that.far);
88 public final Plane getLeft()
90 return this.left;
93 public final Plane getRight()
95 return this.right;
98 public final Plane getBottom()
100 return this.bottom;
103 public final Plane getTop()
105 return this.top;
108 public final Plane getNear()
110 return this.near;
113 public final Plane getFar()
115 return this.far;
118 public int hashCode()
120 int result;
121 result = this.left.hashCode();
122 result = 31 * result + this.right.hashCode();
123 result = 19 * result + this.bottom.hashCode();
124 result = 23 * result + this.top.hashCode();
125 result = 17 * result + this.near.hashCode();
126 result = 19 * result + this.far.hashCode();
128 return result;
131 public String toString()
133 StringBuilder sb = new StringBuilder();
134 sb.append("(");
135 sb.append("left=").append(this.near);
136 sb.append(", right=").append(this.near);
137 sb.append(", bottom=").append(this.near);
138 sb.append(", top=").append(this.near);
139 sb.append(", near=").append(this.near);
140 sb.append(", far=").append(this.near);
141 sb.append(")");
142 return sb.toString();
145 // ============== Factory Functions ======================= //
146 // ============== Factory Functions ======================= //
147 // ============== Factory Functions ======================= //
149 public static Frustum fromProjectionMatrix(Matrix projectionMatrix)
151 Matrix m = projectionMatrix;
152 if (m == null)
154 String message = WorldWind.retrieveErrMsg("nullValue.MatrixIsNull");
155 WorldWind.logger().log(java.util.logging.Level.FINE, message);
156 throw new IllegalArgumentException(message);
159 // Extract the left clipping plane from the projectionMatrix-matrix.
160 double leftMag = Math.sqrt((m.m41 + m.m11) * (m.m41 + m.m11) + (m.m42 + m.m12) * (m.m42 + m.m12)
161 + (m.m43 + m.m13) * (m.m43 + m.m13));
162 Plane leftPlane = new Plane((m.m41 + m.m11) / leftMag, (m.m42 + m.m12) / leftMag, (m.m43 + m.m13) / leftMag,
163 m.m44 + m.m14);
164 // Extract the right clipping plane from the projectionMatrix-matrix.
165 double rightMag = Math.sqrt((m.m41 - m.m11) * (m.m41 - m.m11) + (m.m42 - m.m12) * (m.m42 - m.m12)
166 + (m.m43 - m.m13) * (m.m43 - m.m13));
167 Plane rightPlane = new Plane((m.m41 - m.m11) / rightMag, (m.m42 - m.m12) / rightMag, (m.m43 - m.m13) / rightMag,
168 m.m44 - m.m14);
169 // Extract the bottom clipping plane from the projectionMatrix-matrix.
170 double bottomMag = Math.sqrt((m.m41 + m.m21) * (m.m41 + m.m21) + (m.m42 + m.m22) * (m.m42 + m.m22)
171 + (m.m43 + m.m23) * (m.m43 + m.m23));
172 Plane bottomPlane = new Plane((m.m41 + m.m21) / bottomMag, (m.m42 + m.m22) / bottomMag,
173 (m.m43 + m.m23) / bottomMag,
174 m.m44 + m.m24);
175 // Extract the top clipping plane from the projectionMatrix-matrix.
176 double topMag = Math.sqrt((m.m41 - m.m21) * (m.m41 - m.m21) + (m.m42 - m.m22) * (m.m42 - m.m22)
177 + (m.m43 - m.m23) * (m.m43 - m.m23));
178 Plane topPlane = new Plane((m.m41 - m.m21) / topMag, (m.m42 - m.m22) / topMag, (m.m43 - m.m23) / topMag,
179 m.m44 - m.m24);
180 // Extract the near clipping plane from the projectionMatrix-matrix.
181 double nearMag = Math.sqrt((m.m41 + m.m31) * (m.m41 + m.m31) + (m.m42 + m.m32) * (m.m42 + m.m32)
182 + (m.m43 + m.m33) * (m.m43 + m.m33));
183 Plane nearPlane = new Plane((m.m41 + m.m31) / nearMag, (m.m42 + m.m32) / nearMag, (m.m43 + m.m33) / nearMag,
184 m.m44 + m.m34);
185 // Extract the far clipping plane from the projectionMatrix-matrix.
186 double farMag = Math.sqrt((m.m41 - m.m31) * (m.m41 - m.m31) + (m.m42 - m.m32) * (m.m42 - m.m32)
187 + (m.m43 - m.m33) * (m.m43 - m.m33));
188 Plane farPlane = new Plane((m.m41 - m.m31) / farMag, (m.m42 - m.m32) / farMag, (m.m43 - m.m33) / farMag,
189 m.m44 - m.m34);
190 return new Frustum(leftPlane, rightPlane, bottomPlane, topPlane, nearPlane, farPlane);
194 * Creates a <code>Frustum</code> from a horizontal field-of-view, viewport aspect ratio and distance to near and
195 * far depth clipping planes. The near plane must be closer than the far plane, and both planes must be a positive
196 * distance away.
198 * @param horizontalFieldOfView horizontal field-of-view angle in the range (0, 180)
199 * @param viewportWidth the width of the viewport in screen pixels
200 * @param viewportHeight the height of the viewport in screen pixels
201 * @param near distance to the near depth clipping plane
202 * @param far distance to far depth clipping plane
203 * @throws IllegalArgumentException if fov is not in the range (0, 180), if either near or far are negative, or near
204 * is greater than or equal to far
206 public static Frustum fromPerspective(Angle horizontalFieldOfView, int viewportWidth, int viewportHeight,
207 double near, double far)
209 if (horizontalFieldOfView == null)
211 String message = WorldWind.retrieveErrMsg("geom.ViewFrustum.FieldOfViewIsNull");
212 WorldWind.logger().log(Level.FINE, message);
213 throw new IllegalArgumentException(message);
215 double fov = horizontalFieldOfView.getDegrees();
216 double farMinusNear = far - near;
217 String message = null;
218 if (fov <= 0 || fov > 180)
219 message = WorldWind.retrieveErrMsg("geom.ViewFrustum.FieldOfViewOutOfRange");
220 if (near <= 0 || farMinusNear <= 0)
221 message = WorldWind.retrieveErrMsg("geom.ViewFrusutm.ClippingDistanceOutOfRange");
222 if (message != null)
224 WorldWind.logger().log(java.util.logging.Level.FINE, message);
225 throw new IllegalArgumentException(message);
228 double focalLength = 1d / horizontalFieldOfView.tanHalfAngle();
229 double aspect = viewportHeight / (double) viewportWidth;
230 double lrLen = Math.sqrt(focalLength * focalLength + 1);
231 double btLen = Math.sqrt(focalLength * focalLength + aspect * aspect);
232 Plane leftPlane = new Plane(focalLength / lrLen, 0d, 0d - 1d / lrLen, 0);
233 Plane rightPlane = new Plane(0d - focalLength / lrLen, 0d, 0d - 1d / lrLen, 0d);
234 Plane bottomPlane = new Plane(0d, focalLength / btLen, 0d - aspect / btLen, 0d);
235 Plane topPlane = new Plane(0d, 0d - focalLength / btLen, 0d - aspect / btLen, 0d);
236 Plane nearPlane = new Plane(0d, 0d, 0d - 1d, 0d - near);
237 Plane farPlane = new Plane(0d, 0d, 1d, far);
238 return new Frustum(leftPlane, rightPlane, bottomPlane, topPlane, nearPlane, farPlane);
242 * Creates a <code>Frustum</code> from three sets of parallel clipping planes (a parallel projectionMatrix). In this
243 * case, the near and far depth clipping planes may be a negative distance away.
245 * @param near distance to the near depth clipping plane
246 * @param far distance to far depth clipping plane
247 * @param width horizontal dimension of the near clipping plane
248 * @param height vertical dimension of the near clipping plane
249 * @throws IllegalArgumentException if the difference of any plane set (lright - left, top - bottom, far - near) is
250 * less than or equal to zero.
252 public static Frustum fromPerspective(double width, double height, double near, double far)
254 double farMinusNear = far - near;
255 if (farMinusNear <= 0.0 || width <= 0.0 || height <= 0.0)
257 String message = WorldWind.retrieveErrMsg("geom.ViewFrusutm.ClippingDistanceOutOfRange");
258 WorldWind.logger().log(Level.FINE, message);
259 throw new IllegalArgumentException(message);
262 double width_over_2 = width / 2.0;
263 double height_over_2 = height / 2.0;
264 Plane leftPlane = new Plane(1.0, 0.0, 0.0, width_over_2);
265 Plane rightPlane = new Plane(-1.0, 0.0, 0.0, width_over_2);
266 Plane bottomPlane = new Plane(0.0, 1.0, 0.0, height_over_2);
267 Plane topPlane = new Plane(0.0, -1.0, 0.0, height_over_2);
268 Plane nearPlane = new Plane(0.0, 0.0, -1.0, (near < 0.0) ? near : -near);
269 Plane farPlane = new Plane(0.0, 0.0, 1.0, (far < 0.0) ? -far : far);
270 return new Frustum(leftPlane, rightPlane, bottomPlane, topPlane, nearPlane, farPlane);
273 // ============== Intersection Functions ======================= //
274 // ============== Intersection Functions ======================= //
275 // ============== Intersection Functions ======================= //
278 * @param extent
279 * @return
280 * @throws IllegalArgumentException if <code>extent</code> is null
282 public final boolean intersects(Extent extent)
284 if (extent == null)
286 String msg = WorldWind.retrieveErrMsg("nullValue.ExtentIsNull");
287 WorldWind.logger().log(Level.FINE, msg);
288 throw new IllegalArgumentException(msg);
291 // See if the extent's bounding sphere is within or intersects the frustum.
292 Vec4 c = extent.getCenter();
293 double nr = -extent.getRadius();
295 if (this.far.dot(c) <= nr)
296 return false;
297 if (this.left.dot(c) <= nr)
298 return false;
299 if (this.right.dot(c) <= nr)
300 return false;
301 if (this.top.dot(c) <= nr)
302 return false;
303 if (this.bottom.dot(c) <= nr)
304 return false;
305 //noinspection RedundantIfStatement
306 if (this.near.dot(c) <= nr)
307 return false;
309 return true;
313 * @param point
314 * @return
315 * @throws IllegalArgumentException if <code>point</code> is null
317 public final boolean contains(Vec4 point)
319 if (point == null)
321 String msg = WorldWind.retrieveErrMsg("nullValue.PointIsNull");
322 WorldWind.logger().log(Level.FINE, msg);
323 throw new IllegalArgumentException(msg);
326 if (this.far.dot(point) < 0)
327 return false;
328 if (this.left.dot(point) < 0)
329 return false;
330 if (this.right.dot(point) < 0)
331 return false;
332 if (this.top.dot(point) < 0)
333 return false;
334 if (this.bottom.dot(point) < 0)
335 return false;
336 //noinspection RedundantIfStatement
337 if (this.near.dot(point) < 0)
338 return false;
340 return true;
343 // ============== Geometric Functions ======================= //
344 // ============== Geometric Functions ======================= //
345 // ============== Geometric Functions ======================= //
348 * @param matrix
349 * @return
350 * @throws IllegalArgumentException if <code>matrix</code> is null
352 public Frustum transformBy(Matrix matrix)
354 ensureNonNull(matrix);
355 Plane left = new Plane(this.left.getVector().transformBy4(matrix));
356 Plane right = new Plane(this.right.getVector().transformBy4(matrix));
357 Plane bottom = new Plane(this.bottom.getVector().transformBy4(matrix));
358 Plane top = new Plane(this.top.getVector().transformBy4(matrix));
359 Plane near = new Plane(this.near.getVector().transformBy4(matrix));
360 Plane far = new Plane(this.far.getVector().transformBy4(matrix));
361 return new Frustum(left, right, bottom, top, near, far);
364 // ============== Helper Functions ======================= //
365 // ============== Helper Functions ======================= //
366 // ============== Helper Functions ======================= //
368 private static void ensureNonNull(Matrix matrix)
370 if (matrix == null)
372 String msg = WorldWind.retrieveErrMsg("nullValue.MatrixIsNull");
373 WorldWind.logger().log(Level.FINE, msg);
374 throw new IllegalArgumentException(msg);