Worldwind public release 0.2.1
[worldwind-tracker.git] / gov / nasa / worldwind / BasicOrbitView.java
blob68d2fea41f51a89dbb6bc98443f8b6efa51a46dd
1 /*
2 Copyright (C) 2001, 2006 United States Government as represented by
3 the Administrator of the National Aeronautics and Space Administration.
4 All Rights Reserved.
5 */
6 package gov.nasa.worldwind;
8 import gov.nasa.worldwind.geom.*;
10 import java.util.logging.Level;
12 /**
13 * @author dcollins
14 * @version $Id: BasicOrbitView.java 2126 2007-06-21 21:00:42Z dcollins $
16 public class BasicOrbitView extends AbstractView
18 // Viewing attributes.
19 private Matrix modelView = Matrix.IDENTITY;
20 private Matrix projection = Matrix.IDENTITY;
21 private Frustum frustum = null;
22 private double collisionRadius = -1;
23 // Attribute constants.
24 private static final double MIN_NEAR_CLIP_DISTANCE = 10.0;
26 // ============== Viewing State ======================= //
27 // ============== Viewing State ======================= //
28 // ============== Viewing State ======================= //
30 protected void doApply(DrawContext dc)
32 // Update viewing state.
33 if (!dc.isPickingMode())
35 this.updateViewingState(dc);
36 this.updateAttributes(dc);
38 // Set current GL viewing state.
39 this.loadModelViewProjection(dc, this.modelView, this.projection);
42 protected void doInitialize(DrawContext dc)
44 this.initializeAttributes(dc);
47 public Frustum getFrustum()
49 return this.frustum;
52 private void updateViewingState(DrawContext dc)
54 // Update the model-view matrix.
55 this.modelView = this.computeModelViewMatrix(dc);
57 // Compute current viewing attributes.
58 Vec4 eyeVec = Vec4.UNIT_W.transformBy4(this.modelView.getInverse());
59 double nearClipDist = this.computeNearClipDistance(dc, eyeVec);
60 double farClipDist = this.computeFarClipDistance(dc, eyeVec);
62 Angle fov = this.getFieldOfView();
63 java.awt.Rectangle viewport = this.getViewport();
64 // Update the frustum and projection-matrix from a standard perspective projection.
65 this.projection = Matrix.fromPerspective(
66 fov,
67 viewport.width, viewport.height,
68 nearClipDist, farClipDist);
69 this.frustum = Frustum.fromPerspective(
70 fov, viewport.width, viewport.height,
71 nearClipDist, farClipDist);
73 // Update the collision radius.
74 if (this.collisionRadius < 0.0)
75 this.collisionRadius = this.computeCollisionRadius(MIN_NEAR_CLIP_DISTANCE);
78 // ============== Attribute Accessors ======================= //
79 // ============== Attribute Accessors ======================= //
80 // ============== Attribute Accessors ======================= //
82 // Geographic coordinate data.
83 private Angle focusLat = null;
84 private Angle focusLon = null;
85 private double eyeDist = -1;
86 private Angle heading = null;
87 private Angle pitch = null;
88 private double altitude = -1;
90 private void initializeAttributes(DrawContext dc)
92 this.focusLat = getInitialLatitude(dc, this.focusLat);
93 this.focusLon = getInitialLongitude(dc, this.focusLon);
94 this.altitude = getInitialAltitude(dc, this.altitude);
95 this.eyeDist = this.altitude;
96 this.heading = getInitialHeading(dc, this.heading);
97 this.pitch = getInitialPitch(dc, this.pitch);
100 private void updateAttributes(DrawContext dc)
104 public Angle getLatitude()
106 return this.focusLat;
109 public void setLatitude(Angle newLatitude)
111 if (newLatitude == null)
113 String message = WorldWind.retrieveErrMsg("nullValue.AngleIsNull");
114 WorldWind.logger().log(Level.FINE, message);
115 throw new IllegalArgumentException(message);
117 this.focusLat = this.normalizeLatitude(newLatitude);
120 public Angle getLongitude()
122 return this.focusLon;
125 public void setLongitude(Angle newLongitude)
127 if (newLongitude == null)
129 String message = WorldWind.retrieveErrMsg("nullValue.AngleIsNull");
130 WorldWind.logger().log(Level.FINE, message);
131 throw new IllegalArgumentException(message);
133 this.focusLon = this.normalizeLongitude(newLongitude);
136 public double getAltitude()
138 return this.altitude;
141 public void setAltitude(double newAltitude)
143 throw new UnsupportedOperationException();
146 public void setLatLon(LatLon newLatLon)
148 if (newLatLon == null)
150 String message = WorldWind.retrieveErrMsg("nullValue.LatLonIsNull");
151 WorldWind.logger().log(Level.FINE, message);
152 throw new IllegalArgumentException(message);
154 this.setLatitude(newLatLon.getLatitude());
155 this.setLongitude(newLatLon.getLongitude());
158 public void setLatLonAltitude(Position newPosition)
160 throw new UnsupportedOperationException();
163 public Angle getHeading()
165 return this.heading;
168 public void setHeading(Angle newHeading)
170 if (newHeading == null)
172 String message = WorldWind.retrieveErrMsg("nullValue.AngleIsNull");
173 WorldWind.logger().log(Level.FINE, message);
174 throw new IllegalArgumentException(message);
176 this.heading = this.normalizeHeading(newHeading);
179 public Angle getPitch()
181 return this.pitch;
184 public void setPitch(Angle newPitch)
186 if (newPitch == null)
188 String message = WorldWind.retrieveErrMsg("nullValue.AngleIsNull");
189 WorldWind.logger().log(Level.FINE, message);
190 throw new IllegalArgumentException(message);
192 this.pitch = this.normalizePitch(newPitch);
195 public Angle getRoll()
197 return Angle.ZERO;
200 public void setRoll(Angle newRoll)
204 public double getZoom()
206 return this.eyeDist;
209 public void setZoom(double newZoom)
211 this.eyeDist = this.normalizeZoom(newZoom);
214 // ============== Attribute Normalizing ======================= //
215 // ============== Attribute Normalizing ======================= //
216 // ============== Attribute Normalizing ======================= //
218 public Angle normalizeLatitude(Angle latitude)
220 return clamp(latitude, Angle.NEG90, Angle.POS90);
223 public Angle normalizeLongitude(Angle longitude)
225 return normalize(longitude, Angle.NEG180, Angle.POS180);
228 public double normalizeAltitude(double altitude)
230 return altitude;
233 public Angle normalizeHeading(Angle heading)
235 if (heading == null)
237 String message = WorldWind.retrieveErrMsg("nullValue.AngleIsNull");
238 WorldWind.logger().log(Level.FINE, message);
239 throw new IllegalArgumentException(message);
241 return normalize(heading, Angle.ZERO, Angle.POS360);
244 public Angle normalizePitch(Angle pitch)
246 return clamp(pitch, Angle.ZERO, Angle.POS90);
249 public Angle normalizeRoll(Angle roll)
251 return roll;
254 public double normalizeZoom(double zoom)
256 return Math.max(this.collisionRadius, zoom);
259 private static Angle clamp(Angle value, Angle min, Angle max)
261 if (value.compareTo(min) < 0)
262 return min;
263 else if (value.compareTo(max) > 0)
264 return max;
265 return value;
268 private static Angle normalize(Angle value, Angle min, Angle max)
270 if (value.compareTo(min) < 0)
271 return value.add(max).subtract(min);
272 else if (value.compareTo(max) > 0)
273 return value.subtract(max).add(min);
274 return value;
277 // ============== Viewing Transforms ======================= //
278 // ============== Viewing Transforms ======================= //
279 // ============== Viewing Transforms ======================= //
281 private Matrix computeModelViewMatrix(DrawContext dc)
283 Globe globe = dc.getGlobe();
284 if (globe == null)
285 return null;
287 Vec4 focusPoint = globe.computePointFromPosition(this.focusLat, this.focusLon, 0);
288 if (focusPoint == null)
290 String message = WorldWind.retrieveErrMsg("BasicOrbitView.NullSurfacePoint");
291 WorldWind.logger().log(Level.FINE, message);
292 throw new IllegalStateException(message);
295 Matrix modelView = lookAt(this.focusLat, this.focusLon, focusPoint.getLength3(),
296 this.eyeDist, this.heading, this.pitch);
297 Vec4 eyeVec = Vec4.UNIT_W.transformBy4(modelView.getInverse());
298 Position polarEye = globe.computePositionFromPoint(eyeVec);
300 Vec4 surfacePoint = computeSurfacePoint(dc, polarEye.getLatitude(), polarEye.getLongitude());
301 if (surfacePoint != null)
303 double distanceToSurface = eyeVec.getLength3() - this.collisionRadius - surfacePoint.getLength3();
304 if (distanceToSurface < 0.0)
306 Vec4 surfaceNormal = eyeVec.normalize3();
307 Vec4 newEye = Vec4.fromLine3(Vec4.ZERO, eyeVec.getLength3() - distanceToSurface, surfaceNormal);
308 Vec4 forward = eyeVec.subtract3(focusPoint);
309 Vec4 newForward = newEye.subtract3(focusPoint);
310 double dot = forward.dot3(newForward) / (forward.getLength3() * newForward.getLength3());
311 if (dot >= -1.0 && dot <= 1.0)
313 double pitchChange = Math.acos(dot);
314 this.pitch = clamp(this.pitch.subtract(Angle.fromRadians(pitchChange)), Angle.ZERO, Angle.POS90);
315 this.eyeDist = Math.max(this.collisionRadius, newForward.getLength3());
316 modelView = lookAt(this.focusLat, this.focusLon, focusPoint.getLength3(), this.eyeDist,
317 this.heading, this.pitch);
322 // Compute the current eyeVec altitude above sea level (Globe radius).
323 eyeVec = Vec4.UNIT_W.transformBy4(modelView.getInverse());
324 polarEye = globe.computePositionFromPoint(eyeVec);
325 this.altitude = eyeVec.getLength3() - globe.getRadiusAt(polarEye.getLatitude(), polarEye.getLongitude());
327 return modelView;
330 private static Matrix lookAt(Angle focusX, Angle focusY, double focusDistance,
331 double tiltDistance, Angle tiltZ, Angle tiltX)
333 Matrix m = Matrix.IDENTITY;
334 // Translate model away from eye.
335 m = m.multiply(Matrix.fromTranslation(0, 0, -tiltDistance));
336 // Apply tilt by rotating about X axis at pivot point.
337 m = m.multiply(Matrix.fromRotationX(tiltX.multiply(-1)));
338 m = m.multiply(Matrix.fromRotationZ(tiltZ));
339 m = m.multiply(Matrix.fromTranslation(0, 0, -focusDistance));
340 // Rotate model to lat/lon of eye point.
341 m = m.multiply(Matrix.fromRotationX(focusX));
342 m = m.multiply(Matrix.fromRotationY(focusY.multiply(-1)));
343 return m;
346 // ============== Utilities ======================= //
347 // ============== Utilities ======================= //
348 // ============== Utilities ======================= //
350 private double computeNearClipDistance(DrawContext dc, Vec4 eyeVec)
352 Angle fov = this.getFieldOfView();
353 if (fov == null)
354 return MIN_NEAR_CLIP_DISTANCE;
356 // Compute the most distant near clipping plane.
357 double tanHalfFov = fov.tanHalfAngle();
358 return Math.max(
359 MIN_NEAR_CLIP_DISTANCE,
360 this.altitude / (2 * Math.sqrt(2 * tanHalfFov * tanHalfFov + 1)));
363 private double computeFarClipDistance(DrawContext dc, Vec4 eyeVec)
365 // Compute the closest allowable far clipping plane distance.
366 return this.computeHorizonDistance(dc.getGlobe(), dc.getVerticalExaggeration(), eyeVec);
369 private double computeCollisionRadius(double nearClipDistance)
371 final double MIN_COLLISION_RADIUS = 1;
373 java.awt.Rectangle viewport = this.getViewport();
374 Angle fov = this.getFieldOfView();
375 if (viewport == null || fov == null)
376 return MIN_COLLISION_RADIUS;
378 if (nearClipDistance <= 0)
379 nearClipDistance = MIN_COLLISION_RADIUS;
381 // Compute the distance between the eye, and any corner on the near clipping rectangle.
382 double tanHalfFov = fov.tanHalfAngle();
383 double clipRectX = nearClipDistance * tanHalfFov;
384 double clipRectY = viewport.height * clipRectX / (double) viewport.width;
385 return 1 + Math.sqrt(clipRectX * clipRectX + clipRectY * clipRectY + nearClipDistance * nearClipDistance);
388 private static Vec4 computeSurfacePoint(DrawContext dc, Angle lat, Angle lon)
390 SectorGeometryList sg = dc.getSurfaceGeometry();
391 if (sg != null)
393 Vec4 vec = sg.getSurfacePoint(lat, lon);
394 if (vec != null)
395 return vec;
398 Globe globe = dc.getGlobe();
399 if (globe == null)
400 return null;
402 double elevation = dc.getVerticalExaggeration() * globe.getElevation(lat, lon);
403 return globe.computePointFromPosition(lat, lon, elevation);
406 public LatLon computeVisibleLatLonRange()
408 return null; // TODO