Worldwind public release 0.2
[worldwind-tracker.git] / gov / nasa / worldwind / awt / InterpolatorTimer.java
blob4b4a54660b931d4974f49915a2110de80ab149db
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.awt;
8 import gov.nasa.worldwind.*;
9 import gov.nasa.worldwind.geom.*;
11 /**
12 * @author dcollins
13 * @version $Id: InterpolatorTimer.java 1818 2007-05-10 15:59:46Z dcollins $
15 class InterpolatorTimer
17 static class ViewProperties
19 public LatLon latLon;
20 public Angle heading;
21 public Angle pitch;
22 public Double zoom;
25 private java.util.TimerTask timerTask;
26 private volatile boolean isRunning;
27 private java.beans.PropertyChangeListener listener;
28 private double stepCoefficient;
29 private double errorThreshold;
30 private Object begin;
31 private Object end;
33 public InterpolatorTimer(final int period)
35 if (period < 0)
37 String message = WorldWind.retrieveErrMsg("awt.InterpolatorTimer.PeriodLessThanZero");
38 WorldWind.logger().log(java.util.logging.Level.FINE, message);
39 throw new IllegalArgumentException(message);
41 java.util.Timer timer = new java.util.Timer();
42 this.timerTask = new java.util.TimerTask()
44 public void run()
46 long time = System.currentTimeMillis();
47 if (time - this.scheduledExecutionTime() >= 2 * period)
48 return;
49 if (InterpolatorTimer.this.listener == null)
50 return;
51 InterpolatorTimer.this.updateAndNotify(InterpolatorTimer.this.listener);
54 timer.schedule(timerTask, 0, period);
57 public synchronized boolean isRunning()
59 return this.isRunning;
62 private static double error(Object x, Object end)
64 if (x instanceof Angle)
65 return error((Angle) x, (Angle) end);
66 else if (x instanceof Double)
67 return error((Double) x, (Double) end);
68 else if (x instanceof LatLon)
69 return error((LatLon) x, (LatLon) end);
70 else if (x instanceof Point)
71 return error((Point) x, (Point) end);
72 else if (x instanceof ViewProperties)
73 return error((ViewProperties) x, (ViewProperties) end);
74 else
75 return 0;
78 private static double error(Double x, Double end)
80 return Math.abs(end - x);
83 private static double error(Angle x, Angle end)
85 return Math.abs(end.getDegrees() - x.getDegrees()) % 360d;
88 private static double error(LatLon x, LatLon end)
90 double latError = Math.abs(end.getLatitude().getRadians() - x.getLatitude().getRadians()) % (Math.PI / 2);
91 double lonError = Math.abs(end.getLongitude().getRadians() - x.getLongitude().getRadians()) % Math.PI;
92 return latError > lonError ? latError : lonError;
95 private static double error(Point x, Point end)
97 double maxError = 0;
98 maxError = Math.max(maxError, error(x.x(), end.x()));
99 maxError = Math.max(maxError, error(x.y(), end.y()));
100 maxError = Math.max(maxError, error(x.z(), end.z()));
101 maxError = Math.max(maxError, error(x.w(), end.w()));
102 return maxError;
105 private static double error(ViewProperties x, ViewProperties end)
107 double maxError = 0;
108 if (x.latLon != null && end.latLon != null)
109 maxError = Math.max(maxError, error(x.latLon, end.latLon));
110 if (x.heading != null && end.heading != null)
111 maxError = Math.max(maxError, error(x.heading, end.heading));
112 if (x.pitch != null && end.pitch != null)
113 maxError = Math.max(maxError, error(x.pitch, end.pitch));
114 if (x.zoom != null && end.zoom != null)
115 maxError = Math.max(maxError, error(x.zoom, end.zoom));
116 return maxError;
119 private static Object mix(double t, Object begin, Object end)
121 if (begin instanceof Angle)
122 return mix(t, (Angle) begin, (Angle) end);
123 else if (begin instanceof Double)
124 return mix(t, (Double) begin, (Double) end);
125 else if (begin instanceof LatLon)
126 return mix(t, (LatLon) begin, (LatLon) end);
127 else if (begin instanceof Point)
128 return mix(t, (Point) begin, (Point) end);
129 else if (begin instanceof ViewProperties)
130 return mix(t, (ViewProperties) begin, (ViewProperties) end);
131 else
132 return null;
135 private static Angle mix(double t, Angle begin, Angle end)
137 if (t < 0)
138 return begin;
139 else if (t > 1)
140 return end;
141 Quaternion beginQuat = Quaternion.EulerToQuaternion(begin.getRadians(), 0, 0);
142 Quaternion endQuat = Quaternion.EulerToQuaternion(end.getRadians(), 0, 0);
143 Quaternion q = Quaternion.Slerp(beginQuat, endQuat, t);
144 Point v = Quaternion.QuaternionToEuler(q);
146 if (Double.isNaN(v.x()))
147 return null;
149 return Angle.fromRadians(v.x());
152 private static Double mix(double t, Double begin, Double end)
154 if (t < 0)
155 return begin;
156 else if (t > 1)
157 return end;
158 return (1 - t) * begin + t * end;
161 private static LatLon mix(double t, LatLon begin, LatLon end)
163 if (t < 0)
164 return begin;
165 else if (t > 1)
166 return end;
167 Quaternion beginQuat = Quaternion.EulerToQuaternion(begin.getLongitude().getRadians(),
168 begin.getLatitude().getRadians(), 0);
169 Quaternion endQuat = Quaternion.EulerToQuaternion(end.getLongitude().getRadians(),
170 end.getLatitude().getRadians(), 0);
171 Quaternion q = Quaternion.Slerp(beginQuat, endQuat, t);
172 Point v = Quaternion.QuaternionToEuler(q);
173 if (Double.isNaN(v.x()) || Double.isNaN(v.y()))
174 return null;
175 return LatLon.fromRadians(v.y(), v.x());
178 private static Point mix(double t, Point begin, Point end)
180 return new Point(mix(t, begin.x(), end.x()), mix(t, begin.y(), end.y()),
181 mix(t, begin.z(), end.z()), mix(t, begin.w(), end.w()));
184 private static ViewProperties mix(double t, ViewProperties begin, ViewProperties end)
186 ViewProperties x = new ViewProperties();
187 if (begin.latLon != null && end.latLon != null)
188 x.latLon = mix(t, begin.latLon, end.latLon);
189 if (begin.heading != null && end.heading != null)
191 if (begin.pitch != null && end.pitch != null)
193 // TODO: this doesn't pan heading & pitch equally
194 Quaternion beginQuat = Quaternion.EulerToQuaternion(begin.heading.getRadians(),
195 begin.pitch.getRadians(), 0);
196 Quaternion endQuat = Quaternion.EulerToQuaternion(end.heading.getRadians(),
197 end.pitch.getRadians(), 0);
198 Quaternion q = Quaternion.Slerp(beginQuat, endQuat, t);
199 Point v = Quaternion.QuaternionToEuler(q);
200 if (!Double.isNaN(v.x()) && !Double.isNaN(v.y()))
202 x.heading = Angle.fromRadians(v.x());
203 x.pitch = Angle.fromRadians(v.y());
205 else
207 x.heading = null;
208 x.pitch = null;
211 else
213 x.heading = mix(t, begin.heading, end.heading);
216 else if (begin.pitch != null && end.pitch != null)
217 x.pitch = mix(t, begin.pitch, end.pitch);
218 if (begin.zoom != null && end.zoom != null)
219 x.zoom = mix(t, begin.zoom, end.zoom);
220 return x;
223 public synchronized void start(double stepCoefficient, double errorThreshold, Object begin, Object end,
224 java.beans.PropertyChangeListener listener)
226 if (stepCoefficient < 0)
228 String message = WorldWind.retrieveErrMsg("awt.InterpolatorTimer.StepCoefficientLessThanZero");
229 WorldWind.logger().log(java.util.logging.Level.FINE, message);
230 throw new IllegalArgumentException(message);
232 if (errorThreshold < 0)
234 String message = WorldWind.retrieveErrMsg("awt.InterpolatorTimer.ErrorThresholdLessThanZero");
235 WorldWind.logger().log(java.util.logging.Level.FINE, message);
236 throw new IllegalArgumentException(message);
238 if (begin == null || end == null)
240 String message = WorldWind.retrieveErrMsg("nullValue.ObjectIsNull");
241 WorldWind.logger().log(java.util.logging.Level.FINE, message);
242 throw new IllegalArgumentException(message);
244 if (!end.getClass().isInstance(begin))
246 String message = WorldWind.retrieveErrMsg("awt.InterpolatorTimer.DifferentMixTypes");
247 WorldWind.logger().log(java.util.logging.Level.FINE, message);
248 throw new IllegalArgumentException(message);
250 if (listener == null)
252 String message = WorldWind.retrieveErrMsg("nullValue.ListenerIsNull");
253 WorldWind.logger().log(java.util.logging.Level.FINE, message);
254 throw new IllegalArgumentException(message);
257 if (this.isRunning && this.listener != null && !this.listener.equals(listener))
258 this.listener.propertyChange(new java.beans.PropertyChangeEvent(this, null, null, null));
260 this.listener = listener;
261 this.stepCoefficient = stepCoefficient;
262 this.errorThreshold = errorThreshold;
263 this.begin = begin;
264 this.end = end;
265 this.isRunning = true;
268 public synchronized void stop()
270 if (this.isRunning)
272 if (this.listener != null)
273 listener.propertyChange(new java.beans.PropertyChangeEvent(this, null, null, null));
274 this.listener = null;
275 this.stepCoefficient = errorThreshold = -1;
276 this.begin = end = null;
277 this.isRunning = false;
281 private synchronized void updateAndNotify(java.beans.PropertyChangeListener listener)
283 if (!this.isRunning || this.begin == null || listener == null)
284 return;
286 Object newValue = mix(this.stepCoefficient, this.begin, this.end);
287 if (newValue == null)
289 this.stop();
290 return;
293 double error = error(newValue, this.end);
294 if (error < this.errorThreshold)
296 this.stop();
297 return;
300 listener.propertyChange(new java.beans.PropertyChangeEvent(this, null, this.begin, newValue));
301 this.begin = newValue;