Updated to worldwind release 20070817
[worldwind-tracker.git] / gov / nasa / worldwind / view / FlyToOrbitViewStateIterator.java
blobd9646f4659593b2dc5594f2f987bc7223d62f618
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.view;
8 import gov.nasa.worldwind.geom.*;
9 import gov.nasa.worldwind.globes.Globe;
10 import gov.nasa.worldwind.util.Logging;
12 /**
13 * @author dcollins
14 * @version $Id: FlyToOrbitViewStateIterator.java 2488 2007-08-02 18:14:08Z dcollins $
16 public class FlyToOrbitViewStateIterator extends BasicOrbitViewStateIterator
18 protected FlyToOrbitViewStateIterator(long lengthMillis, OrbitViewAnimator animator)
20 super(false, new ScheduledOrbitViewInterpolator(lengthMillis), animator);
23 // ============== "Pan To" ======================= //
24 // ============== "Pan To" ======================= //
25 // ============== "Pan To" ======================= //
27 private static class PanAnimator extends BasicOrbitViewAnimator
29 private final OrbitViewAnimator latLonAnimator;
30 private final OrbitViewAnimator zoomAnimator;
31 private final OrbitViewAnimator headingAnimator;
32 private final OrbitViewAnimator pitchAnimator;
33 private final OrbitViewAnimator beginToMidZoomAnimator, endToMidZoomAnimator;
34 private final boolean useMidZoom;
36 private PanAnimator(
37 Globe globe,
38 LatLon beginLookAtLatLon, LatLon endLookAtLatLon,
39 Angle beginHeading, Angle endHeading,
40 Angle beginPitch, Angle endPitch,
41 double beginZoom, double endZoom)
43 // Latitude & Longitude.
44 this.latLonAnimator = new LatLonAnimator(
45 beginLookAtLatLon, endLookAtLatLon,
46 OrbitViewPropertyAccessor.createLookAtLatitudeAndLongitudeAccessor());
47 // Zoom.
48 this.zoomAnimator = new DoubleAnimator(
49 beginZoom, endZoom,
50 OrbitViewPropertyAccessor.createZoomAccessor());
51 // Heading.
52 this.headingAnimator = new AngleAnimator(
53 beginHeading, endHeading,
54 OrbitViewPropertyAccessor.createHeadingAccessor());
55 // Pitch.
56 this.pitchAnimator = new AngleAnimator(
57 beginPitch, endPitch,
58 OrbitViewPropertyAccessor.createPitchAccessor());
60 // Mid-zoom logic.
61 double midZoom = computeMidZoom(
62 globe,
63 beginLookAtLatLon, endLookAtLatLon,
64 beginZoom, endZoom);
65 this.useMidZoom = useMidZoom(
66 beginZoom, endZoom, midZoom);
67 this.beginToMidZoomAnimator = new DoubleAnimator(
68 beginZoom, midZoom,
69 OrbitViewPropertyAccessor.createZoomAccessor());
70 this.endToMidZoomAnimator = new DoubleAnimator(
71 endZoom, midZoom,
72 OrbitViewPropertyAccessor.createZoomAccessor());
75 private static double computeMidZoom(
76 Globe globe,
77 LatLon beginLatLon, LatLon endLatLon,
78 double beginZoom, double endZoom)
80 // Scale factor is angular distance over 180 degrees.
81 Angle sphericalDistance = LatLon.sphericalDistance(beginLatLon, endLatLon);
82 double scaleFactor = angularRatio(sphericalDistance, Angle.POS180);
84 // Mid-point zoom is interpolated value between minimum and maximum zoom.
85 final double MIN_ZOOM = Math.min(beginZoom, endZoom);
86 final double MAX_ZOOM = 3.0 * globe.getRadius();
87 return mixDouble(scaleFactor, MIN_ZOOM, MAX_ZOOM);
90 private static boolean useMidZoom(double beginZoom, double endZoom, double midZoom)
92 double a = Math.abs(endZoom - beginZoom);
93 double b = Math.abs(midZoom - Math.max(beginZoom, endZoom));
94 return a < b;
97 protected void doNextStateImpl(double interpolant, OrbitView orbitView, BasicOrbitViewStateIterator stateIterator)
99 if (orbitView == null)
101 String message = Logging.getMessage("nullValue.OrbitViewIsNull");
102 Logging.logger().severe(message);
103 throw new IllegalArgumentException(message);
105 if (stateIterator == null)
107 String message = Logging.getMessage("nullValue.OrbitViewStateIteratorIsNull");
108 Logging.logger().severe(message);
109 throw new IllegalArgumentException(message);
112 this.nextLatLonState(interpolant, orbitView, stateIterator);
113 this.nextZoomState(interpolant, orbitView, stateIterator);
114 this.nextHeadingState(interpolant, orbitView, stateIterator);
115 this.nextPitchState(interpolant, orbitView, stateIterator);
118 private void nextLatLonState(double interpolant, OrbitView orbitView, BasicOrbitViewStateIterator stateIterator)
120 final int MAX_SMOOTHING = 1;
121 final double LATLON_START = this.useMidZoom ? 0.2 : 0.0;
122 final double LATLON_STOP = this.useMidZoom ? 0.8 : 0.8;
123 double latLonInterpolant = basicInterpolant(interpolant, LATLON_START, LATLON_STOP, MAX_SMOOTHING);
124 this.latLonAnimator.doNextState(latLonInterpolant, orbitView, stateIterator);
128 private void nextHeadingState(double interpolant, OrbitView orbitView, BasicOrbitViewStateIterator stateIterator)
130 final int MAX_SMOOTHING = 1;
131 final double HEADING_START = this.useMidZoom ? 0.0 : 0.6;
132 final double HEADING_STOP = 1.0;
133 double headingInterpolant = basicInterpolant(interpolant, HEADING_START, HEADING_STOP, MAX_SMOOTHING);
134 this.headingAnimator.doNextState(headingInterpolant, orbitView, stateIterator);
137 private void nextPitchState(double interpolant, OrbitView orbitView, BasicOrbitViewStateIterator stateIterator)
139 final int MAX_SMOOTHING = 1;
140 final double PITCH_START = 0.0;
141 final double PITCH_STOP = 0.8;
142 double pitchInterpolant = basicInterpolant(interpolant, PITCH_START, PITCH_STOP, MAX_SMOOTHING);
143 this.pitchAnimator.doNextState(pitchInterpolant, orbitView, stateIterator);
146 private void nextZoomState(double interpolant, OrbitView orbitView, BasicOrbitViewStateIterator stateIterator)
148 final int MAX_SMOOTHING = 1;
149 if (this.useMidZoom)
151 final double ZOOM_START = 0.0;
152 final double ZOOM_STOP = 1.0;
153 double zoomInterpolant = this.zoomInterpolant(interpolant, ZOOM_START, ZOOM_STOP, MAX_SMOOTHING);
154 if (interpolant <= 0.5)
155 this.beginToMidZoomAnimator.doNextState(zoomInterpolant, orbitView, stateIterator);
156 else
157 this.endToMidZoomAnimator.doNextState(zoomInterpolant, orbitView, stateIterator);
159 else
161 final double ZOOM_START = 0.0;
162 final double ZOOM_STOP = 1.0;
163 double zoomInterpolant = basicInterpolant(interpolant, ZOOM_START, ZOOM_STOP, MAX_SMOOTHING);
164 this.zoomAnimator.doNextState(zoomInterpolant, orbitView, stateIterator);
168 private double zoomInterpolant(double interpolant, double startInterpolant, double stopInterpolant,
169 int maxSmoothing)
171 // Map interpolant in to range [start, stop].
172 double normalizedInterpolant = interpolantNormalized(interpolant, startInterpolant, stopInterpolant);
174 // During first half of iteration, zoom increases from begin to mid,
175 // and decreases from mid to end during second half.
176 if (normalizedInterpolant <= 0.5)
178 normalizedInterpolant = 2.0 * normalizedInterpolant;
180 else
182 normalizedInterpolant = 1.0 - (2.0 * normalizedInterpolant - 1.0);
185 return interpolantSmoothed(normalizedInterpolant, maxSmoothing);
189 // ============== "Zoom To" ======================= //
190 // ============== "Zoom To" ======================= //
191 // ============== "Zoom To" ======================= //
193 private static class ZoomAnimator extends BasicOrbitViewAnimator
195 private final OrbitViewAnimator headingAnimator;
196 private final OrbitViewAnimator pitchAnimator;
197 private final OrbitViewAnimator zoomAnimator;
199 private ZoomAnimator(
200 Angle beginHeading, Angle endHeading,
201 Angle beginPitch, Angle endPitch,
202 double beginZoom, double endZoom)
204 // Heading.
205 this.headingAnimator = new AngleAnimator(
206 beginHeading, endHeading,
207 OrbitViewPropertyAccessor.createHeadingAccessor());
208 // Pitch.
209 this.pitchAnimator = new AngleAnimator(
210 beginPitch, endPitch,
211 OrbitViewPropertyAccessor.createPitchAccessor());
212 // Zoom.
213 this.zoomAnimator = new DoubleAnimator(
214 beginZoom, endZoom,
215 OrbitViewPropertyAccessor.createZoomAccessor());
218 protected void doNextStateImpl(double interpolant, OrbitView orbitView, BasicOrbitViewStateIterator stateIterator)
220 if (orbitView == null)
222 String message = Logging.getMessage("nullValue.OrbitViewIsNull");
223 Logging.logger().severe(message);
224 throw new IllegalArgumentException(message);
226 if (stateIterator == null)
228 String message = Logging.getMessage("nullValue.OrbitViewStateIteratorIsNull");
229 Logging.logger().severe(message);
230 throw new IllegalArgumentException(message);
233 this.nextZoomState(interpolant, orbitView, stateIterator);
234 this.nextHeadingState(interpolant, orbitView, stateIterator);
235 this.nextPitchState(interpolant, orbitView, stateIterator);
238 private void nextHeadingState(double interpolant, OrbitView orbitView, BasicOrbitViewStateIterator stateIterator)
240 final int MAX_SMOOTHING = 1;
241 final double HEADING_START = 0.0;
242 final double HEADING_STOP = 0.6;
243 double headingInterpolant = basicInterpolant(interpolant, HEADING_START, HEADING_STOP, MAX_SMOOTHING);
244 this.headingAnimator.doNextState(headingInterpolant, orbitView, stateIterator);
247 private void nextPitchState(double interpolant, OrbitView orbitView, BasicOrbitViewStateIterator stateIterator)
249 final int MAX_SMOOTHING = 1;
250 final double PITCH_START = 0.0;
251 final double PITCH_STOP = 0.6;
252 double pitchInterpolant = basicInterpolant(interpolant, PITCH_START, PITCH_STOP, MAX_SMOOTHING);
253 this.pitchAnimator.doNextState(pitchInterpolant, orbitView, stateIterator);
256 private void nextZoomState(double interpolant, OrbitView orbitView, BasicOrbitViewStateIterator stateIterator)
258 final int MAX_SMOOTHING = 1;
259 final double ZOOM_START = 0.0;
260 final double ZOOM_STOP = 1.0;
261 double zoomInterpolant = basicInterpolant(interpolant, ZOOM_START, ZOOM_STOP, MAX_SMOOTHING);
262 this.zoomAnimator.doNextState(zoomInterpolant, orbitView, stateIterator);
266 // ============== Factory Functions ======================= //
267 // ============== Factory Functions ======================= //
268 // ============== Factory Functions ======================= //
270 public static FlyToOrbitViewStateIterator createPanToIterator(
271 OrbitView orbitView, Globe globe,
272 LatLon lookAtLatLon,
273 Angle heading,
274 Angle pitch,
275 double zoom)
277 if (orbitView == null)
279 String message = Logging.getMessage("nullValue.ViewIsNull");
280 Logging.logger().severe(message);
281 throw new IllegalArgumentException(message);
283 if (globe == null)
285 String message = Logging.getMessage("nullValue.GlobeIsNull");
286 Logging.logger().severe(message);
287 throw new IllegalArgumentException(message);
289 if (lookAtLatLon == null)
291 String message = Logging.getMessage("nullValue.LatLonIsNull");
292 Logging.logger().severe(message);
293 throw new IllegalArgumentException(message);
295 if (heading == null || pitch == null)
297 String message = Logging.getMessage("nullValue.AngleIsNull");
298 Logging.logger().severe(message);
299 throw new IllegalArgumentException(message);
302 Angle beginLookAtLatitude = orbitView.getLookAtLatitude();
303 Angle beginLookAtLongitude = orbitView.getLookAtLongitude();
304 Angle beginHeading = orbitView.getHeading();
305 Angle beginPitch = orbitView.getPitch();
306 double beginZoom = orbitView.getZoom();
307 return createPanToIterator(
308 globe,
309 new LatLon(beginLookAtLatitude, beginLookAtLongitude), lookAtLatLon,
310 beginHeading, heading,
311 beginPitch, pitch,
312 beginZoom, zoom);
315 public static FlyToOrbitViewStateIterator createPanToIterator(
316 Globe globe,
317 LatLon beginLookAtLatLon, LatLon endLookAtLatLon,
318 Angle beginHeading, Angle endHeading,
319 Angle beginPitch, Angle endPitch,
320 double beginZoom, double endZoom)
322 if (globe == null)
324 String message = Logging.getMessage("nullValue.GlobeIsNull");
325 Logging.logger().severe(message);
326 throw new IllegalArgumentException(message);
328 if (beginLookAtLatLon == null || endLookAtLatLon == null)
330 String message = Logging.getMessage("nullValue.LatLonIsNull");
331 Logging.logger().severe(message);
332 throw new IllegalArgumentException(message);
334 if (beginHeading == null || endHeading == null || beginPitch == null || endPitch == null)
336 String message = Logging.getMessage("nullValue.AngleIsNull");
337 Logging.logger().severe(message);
338 throw new IllegalArgumentException(message);
341 // TODO: scale on mid-altitude?
342 final long MIN_LENGTH_MILLIS = 4000;
343 final long MAX_LENGTH_MILLIS = 16000;
344 long lengthMillis = getScaledLengthMillis(
345 beginLookAtLatLon, endLookAtLatLon,
346 MIN_LENGTH_MILLIS, MAX_LENGTH_MILLIS);
347 return createPanToIterator(
348 globe,
349 beginLookAtLatLon, endLookAtLatLon,
350 beginHeading, endHeading,
351 beginPitch, endPitch,
352 beginZoom, endZoom,
353 lengthMillis);
356 public static FlyToOrbitViewStateIterator createPanToIterator(
357 Globe globe,
358 LatLon beginLookAtLatLon, LatLon endLookAtLatLon,
359 Angle beginHeading, Angle endHeading,
360 Angle beginPitch, Angle endPitch,
361 double beginZoom, double endZoom,
362 long lengthMillis)
364 if (globe == null)
366 String message = Logging.getMessage("nullValue.GlobeIsNull");
367 Logging.logger().severe(message);
368 throw new IllegalArgumentException(message);
370 if (beginLookAtLatLon == null || endLookAtLatLon == null)
372 String message = Logging.getMessage("nullValue.AngleIsNull");
373 Logging.logger().severe(message);
374 throw new IllegalArgumentException(message);
376 if (beginHeading == null || endHeading == null || beginPitch == null || endPitch == null)
378 String message = Logging.getMessage("nullValue.AngleIsNull");
379 Logging.logger().severe(message);
380 throw new IllegalArgumentException(message);
382 if (lengthMillis < 0)
384 String message = Logging.getMessage("generic.ArgumentOutOfRange", lengthMillis);
385 Logging.logger().severe(message);
386 throw new IllegalArgumentException(message);
389 OrbitViewAnimator animator = new PanAnimator(
390 globe,
391 beginLookAtLatLon, endLookAtLatLon,
392 beginHeading, endHeading,
393 beginPitch, endPitch,
394 beginZoom, endZoom);
395 return new FlyToOrbitViewStateIterator(lengthMillis, animator);
398 public static FlyToOrbitViewStateIterator createZoomToIterator(
399 OrbitView orbitView,
400 Angle heading, Angle pitch,
401 double zoom)
403 if (orbitView == null)
405 String message = Logging.getMessage("nullValue.ViewIsNull");
406 Logging.logger().severe(message);
407 throw new IllegalArgumentException(message);
409 if (heading == null || pitch == null)
411 String message = Logging.getMessage("nullValue.AngleIsNull");
412 Logging.logger().severe(message);
413 throw new IllegalArgumentException(message);
416 Angle beginHeading = orbitView.getHeading();
417 Angle beginPitch = orbitView.getPitch();
418 double beginZoom = orbitView.getZoom();
419 return createZoomToIterator(
420 beginHeading, heading,
421 beginPitch, pitch,
422 beginZoom, zoom);
425 public static FlyToOrbitViewStateIterator createZoomToIterator(
426 Angle beginHeading, Angle endHeading,
427 Angle beginPitch, Angle endPitch,
428 double beginZoom, double endZoom)
430 if (beginHeading == null || endHeading == null || beginPitch == null || endPitch == null)
432 String message = Logging.getMessage("nullValue.AngleIsNull");
433 Logging.logger().severe(message);
434 throw new IllegalArgumentException(message);
437 final long MIN_LENGTH_MILLIS = 1000;
438 final long MAX_LENGTH_MILLIS = 8000;
439 long lengthMillis = getScaledLengthMillis(
440 beginZoom, endZoom,
441 MIN_LENGTH_MILLIS, MAX_LENGTH_MILLIS);
442 return createZoomToIterator(
443 beginHeading, endHeading,
444 beginPitch, endPitch,
445 beginZoom, endZoom,
446 lengthMillis);
449 public static FlyToOrbitViewStateIterator createZoomToIterator(
450 Angle beginHeading, Angle endHeading,
451 Angle beginPitch, Angle endPitch,
452 double beginZoom, double endZoom,
453 long lengthMillis)
455 if (beginHeading == null || endHeading == null || beginPitch == null || endPitch == null)
457 String message = Logging.getMessage("nullValue.AngleIsNull");
458 Logging.logger().severe(message);
459 throw new IllegalArgumentException(message);
461 if (lengthMillis < 0)
463 String message = Logging.getMessage("generic.ArgumentOutOfRange", lengthMillis);
464 Logging.logger().severe(message);
465 throw new IllegalArgumentException(message);
468 OrbitViewAnimator animator = new ZoomAnimator(
469 beginHeading, endHeading,
470 beginPitch, endPitch,
471 beginZoom, endZoom);
472 return new FlyToOrbitViewStateIterator(lengthMillis, animator);
475 private static long getScaledLengthMillis(
476 double beginZoom, double endZoom,
477 long minLengthMillis, long maxLengthMillis)
479 double scaleFactor = Math.abs(endZoom - beginZoom) / Math.max(endZoom, beginZoom);
480 // Clamp scaleFactor to range [0, 1].
481 scaleFactor = clampDouble(scaleFactor, 0.0, 1.0);
482 // Iteration time is interpolated value between minumum and maximum lengths.
483 return (long) mixDouble(scaleFactor, minLengthMillis, maxLengthMillis);
486 private static long getScaledLengthMillis(
487 LatLon beginLatLon, LatLon endLatLon,
488 long minLengthMillis, long maxLengthMillis)
490 Angle sphericalDistance = LatLon.sphericalDistance(beginLatLon, endLatLon);
491 double scaleFactor = angularRatio(sphericalDistance, Angle.POS180);
492 return (long) mixDouble(scaleFactor, minLengthMillis, maxLengthMillis);
495 // ============== Helper Functions ======================= //
496 // ============== Helper Functions ======================= //
497 // ============== Helper Functions ======================= //
499 // Map amount range [startAmount, stopAmount] to [0, 1] when amount is inside range.
500 private static double interpolantNormalized(double amount, double startAmount, double stopAmount)
502 if (amount < startAmount)
503 return 0.0;
504 else if (amount > stopAmount)
505 return 1.0;
506 return (amount - startAmount) / (stopAmount - startAmount);
509 private static double interpolantSmoothed(double interpolant, int smoothingIterations)
511 // Apply iterative hermite smoothing.
512 double smoothed = interpolant;
513 for (int i = 0; i < smoothingIterations; i++)
515 smoothed = smoothed * smoothed * (3.0 - 2.0 * smoothed);
517 return smoothed;
520 private static double basicInterpolant(double interpolant, double startInterpolant, double stopInterpolant,
521 int maxSmoothing)
523 double normalizedInterpolant = interpolantNormalized(interpolant, startInterpolant, stopInterpolant);
524 return interpolantSmoothed(normalizedInterpolant, maxSmoothing);
527 private static double angularRatio(Angle x, Angle y)
529 if (x == null || y == null)
531 String message = Logging.getMessage("nullValue.AngleIsNull");
532 Logging.logger().severe(message);
533 throw new IllegalArgumentException(message);
536 double unclampedRatio = x.divide(y);
537 return clampDouble(unclampedRatio, 0, 1);
540 private static double clampDouble(double value, double min, double max)
542 return value < min ? min : (value > max ? max : value);
545 private static double mixDouble(double amount, double value1, double value2)
547 if (amount < 0)
548 return value1;
549 else if (amount > 1)
550 return value2;
551 return value1 * (1.0 - amount) + value2 * amount;