2 Copyright (C) 2001, 2006 United States Government as represented by
3 the Administrator of the National Aeronautics and Space Administration.
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
;
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
;
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());
48 this.zoomAnimator
= new DoubleAnimator(
50 OrbitViewPropertyAccessor
.createZoomAccessor());
52 this.headingAnimator
= new AngleAnimator(
53 beginHeading
, endHeading
,
54 OrbitViewPropertyAccessor
.createHeadingAccessor());
56 this.pitchAnimator
= new AngleAnimator(
58 OrbitViewPropertyAccessor
.createPitchAccessor());
61 double midZoom
= computeMidZoom(
63 beginLookAtLatLon
, endLookAtLatLon
,
65 this.useMidZoom
= useMidZoom(
66 beginZoom
, endZoom
, midZoom
);
67 this.beginToMidZoomAnimator
= new DoubleAnimator(
69 OrbitViewPropertyAccessor
.createZoomAccessor());
70 this.endToMidZoomAnimator
= new DoubleAnimator(
72 OrbitViewPropertyAccessor
.createZoomAccessor());
75 private static double computeMidZoom(
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
));
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;
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
);
157 this.endToMidZoomAnimator
.doNextState(zoomInterpolant
, orbitView
, stateIterator
);
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
,
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
;
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
)
205 this.headingAnimator
= new AngleAnimator(
206 beginHeading
, endHeading
,
207 OrbitViewPropertyAccessor
.createHeadingAccessor());
209 this.pitchAnimator
= new AngleAnimator(
210 beginPitch
, endPitch
,
211 OrbitViewPropertyAccessor
.createPitchAccessor());
213 this.zoomAnimator
= new DoubleAnimator(
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
,
277 if (orbitView
== null)
279 String message
= Logging
.getMessage("nullValue.ViewIsNull");
280 Logging
.logger().severe(message
);
281 throw new IllegalArgumentException(message
);
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(
309 new LatLon(beginLookAtLatitude
, beginLookAtLongitude
), lookAtLatLon
,
310 beginHeading
, heading
,
315 public static FlyToOrbitViewStateIterator
createPanToIterator(
317 LatLon beginLookAtLatLon
, LatLon endLookAtLatLon
,
318 Angle beginHeading
, Angle endHeading
,
319 Angle beginPitch
, Angle endPitch
,
320 double beginZoom
, double endZoom
)
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(
349 beginLookAtLatLon
, endLookAtLatLon
,
350 beginHeading
, endHeading
,
351 beginPitch
, endPitch
,
356 public static FlyToOrbitViewStateIterator
createPanToIterator(
358 LatLon beginLookAtLatLon
, LatLon endLookAtLatLon
,
359 Angle beginHeading
, Angle endHeading
,
360 Angle beginPitch
, Angle endPitch
,
361 double beginZoom
, double endZoom
,
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(
391 beginLookAtLatLon
, endLookAtLatLon
,
392 beginHeading
, endHeading
,
393 beginPitch
, endPitch
,
395 return new FlyToOrbitViewStateIterator(lengthMillis
, animator
);
398 public static FlyToOrbitViewStateIterator
createZoomToIterator(
400 Angle heading
, Angle pitch
,
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
,
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(
441 MIN_LENGTH_MILLIS
, MAX_LENGTH_MILLIS
);
442 return createZoomToIterator(
443 beginHeading
, endHeading
,
444 beginPitch
, endPitch
,
449 public static FlyToOrbitViewStateIterator
createZoomToIterator(
450 Angle beginHeading
, Angle endHeading
,
451 Angle beginPitch
, Angle endPitch
,
452 double beginZoom
, double endZoom
,
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
,
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
)
504 else if (amount
> stopAmount
)
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
);
520 private static double basicInterpolant(double interpolant
, double startInterpolant
, double stopInterpolant
,
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
)
551 return value1
* (1.0 - amount
) + value2
* amount
;