/[cits3200i]/branches/playback-branch-rvvs89/UWAPlugins/PlaybackPlugin/Source/PlaybackControl.cs


UCC Code Repository

Contents of /branches/playback-branch-rvvs89/UWAPlugins/PlaybackPlugin/Source/PlaybackControl.cs

Parent Directory Parent Directory | Revision Log Revision Log


Revision 61 - (show annotations) (download)
Thu Sep 30 03:03:02 2010 UTC (11 years, 2 months ago) by rvvs89
File size: 13918 byte(s)
Implemented playback.
Timer resolution and playback bar resolution is currently hardcoded into PlaybackControl.
Removed some listeners from PlaybackControl that were not necessary.
Print current time and time limits to status bar.
Update current position of the playback bar is altered by the user.
If current state is Paused and Rewind or FastForward is pressed, then step back/forward one unit of STEP_SIZE.
Added FollowButton that causes the map to follow playback when checked.
Added FollowIcon for FollowButton.
Increased size of PlaybackDialog to accomodate the new button.
If LoopButton is checked, move back to the startTime if the currentTime exceeds endTime.
If LoopButton is not checked, go to stopped state if the end is reached.
If PlayButton is pressed and currentTime is equal to endTime, move it back to startTime.
If no section of track has been selected, use the entire track.
If a section of track has been selected, use that as the start and end limits for playback.
If a single point on the track has been selected, start playback from that point.
Display markers at the start and end limits.
Don't display markers at the start and end limits if they coincide with the start and end of the track.
On form closing, remove ST event listeners, restore previous selection of track, disable playback timer, hide map markers and dispose forms.
Moved icons that are not compiled in to the executable to a separate directory, Data (start.png, stop.png).
Restructured Plugin to allow other source files to reference it.
Attempt to find icons in the plugin data directory using references from Plugin.
Added PlaybackRouteControlSelection, a simple implementation of IRouteControlSelection to allow PlaybackPlugin to alter the selected area of track thereby allowing playback.
Added a hack to PlaybackControl to prevent problems with Visual Studio's Designer, though the designer still seems to think there are problems with properties.
Added CurrentTime property to PlaybackControl to automatically trigger UI updates when the currentTime is modified.
Added functions to calculate times from the distances provided by SportTracks selection utility since it provides no simple way to convert from distance to time, only from time to distance.
Added listener to SelectedItems on the route control to watch for user interaction, playback is moved to the point the user selected bounded to the current playback limits.
Modified build script to include the Data directory.
Reduced AssemblyInfo version number to 0.1 since this is an alpha.
Removed playing.cs, it doesn't do anything.
Probably some other things too.
1 using PlaybackPlugin.Properties;
2 using System;
3 using System.Collections.Generic;
4 using System.ComponentModel;
5 using System.Data;
6 using System.Drawing;
7 using System.Text;
8 using System.Timers;
9 using System.Windows.Forms;
10 using ZoneFiveSoftware.Common.Data;
11 using ZoneFiveSoftware.Common.Data.Fitness;
12 using ZoneFiveSoftware.Common.Visuals.Mapping;
13 using ZoneFiveSoftware.Common.Visuals.Util;
14 using ZoneFiveSoftware.Common.Data.GPS;
15
16 namespace PlaybackPlugin.Source
17 {
18 public partial class PlaybackControl : UserControl
19 {
20 private PlaybackState state = PlaybackState.Disabled;
21 private IActivity activity = null;
22 private System.Timers.Timer timer;
23 private DateTime startTime;
24 private DateTime endTime;
25 private DateTime currentTime;
26 private Nullable<DateTime> initialSelection = null;
27 private IRouteControl control;
28 private IRouteControlItem item;
29 private IGPSRoute route;
30 private EventHandler selectedItemsChangedHandler;
31 private IList<IRouteControlSelection> oldSelection;
32 private const double MOVE_SELECTION_THRESHOLD = 2000.0;
33 private const double PLAYBACKBAR_TICK_GRANULARITY = 100;
34 private const double REWIND_FACTOR = -5.0;
35 private const double FASTFORWARD_FACTOR = 5.0;
36 private const double STEP_SIZE = 1.0;
37
38 public PlaybackControl()
39 {
40 InitializeComponent();
41 if (PlaybackAnimationRouteControlLayer.Instances.Count == 0)
42 {
43 // HACK: make designer work properly
44 return;
45 }
46 CheckRouteValidity();
47 MarkSelectedRegion();
48 UpdateUI();
49 selectedItemsChangedHandler = new EventHandler(control_SelectedItemsChanged);
50 control.SelectedItemsChanged += selectedItemsChangedHandler;
51 timer = new System.Timers.Timer();
52 timer.Interval = 100.0;
53 timer.Elapsed += new ElapsedEventHandler(PlaybackTick);
54 timer.SynchronizingObject = this;
55 }
56
57 public IActivity Activity
58 {
59 get { return activity; }
60 set {
61 activity = value;
62 State = activity == null ? PlaybackState.Disabled : PlaybackState.Stopped;
63 }
64 }
65
66 public PlaybackState State
67 {
68 get { return state; }
69 set
70 {
71 state = value;
72 timer.Enabled = State == PlaybackState.Playing || State == PlaybackState.FastForwarding || State == PlaybackState.Rewinding;
73 UpdateUI();
74 }
75 }
76
77 private DateTime CurrentTime
78 {
79 get { return currentTime; }
80 set
81 {
82 currentTime = value;
83 currentTime = DateTimeMax(currentTime, startTime);
84 currentTime = DateTimeMin(currentTime, endTime);
85 UpdateUI();
86 }
87 }
88
89 public new void Dispose()
90 {
91 State = PlaybackState.Disabled;
92 control.SelectedItemsChanged -= selectedItemsChangedHandler;
93 control.SelectedItems = oldSelection;
94 timer.Stop();
95 timer.Dispose();
96 PlaybackAnimationRouteControlLayer.Instances[0].StartMarker.Visible = false;
97 PlaybackAnimationRouteControlLayer.Instances[0].EndMarker.Visible = false;
98 PlaybackAnimationRouteControlLayer.Instances[0].UpdateMarkers();
99 base.Dispose();
100 }
101
102 private void UpdateUI()
103 {
104 TimeSpan length = endTime - startTime;
105 TimeSpan progress = currentTime - startTime;
106 rewindButton.Enabled = State != PlaybackState.Disabled && State != PlaybackState.Stopped;
107 fastForwardButton.Enabled = State != PlaybackState.Disabled && State != PlaybackState.Stopped;
108 playButton.Enabled = State != PlaybackState.Disabled;
109 playButton.Image = State == PlaybackState.Playing || State == PlaybackState.FastForwarding || State == PlaybackState.Rewinding ? Resources.PauseIcon : Resources.PlayIcon;
110 loopButton.Enabled = State != PlaybackState.Disabled;
111 speedLabel.Enabled = State != PlaybackState.Disabled;
112 speedSpinner.Enabled = State != PlaybackState.Disabled;
113 statusStrip.Enabled = State != PlaybackState.Disabled;
114 playbackBar.Enabled = State != PlaybackState.Disabled;
115 playbackBar.Maximum = (int) (length.TotalMilliseconds / PLAYBACKBAR_TICK_GRANULARITY);
116 int ticks = Math.Min(playbackBar.Maximum, 10);
117 playbackBar.TickFrequency = playbackBar.Maximum / ticks;
118 playbackBar.Value = (int) (progress.TotalMilliseconds / PLAYBACKBAR_TICK_GRANULARITY);
119 statusLabel.Enabled = State != PlaybackState.Disabled;
120 statusLabel.Text = String.Format("{0}:{1:00}:{2:00} / {3}:{4:00}:{5:00}", progress.Hours, progress.Minutes, progress.Seconds, length.Hours, length.Minutes, length.Seconds);
121 control.SelectedItems = new List<IRouteControlSelection> { new PlaybackRouteControlSelection(item, currentTime) };
122 if (followButton.Checked)
123 {
124 control.MapControl.Center = route.GetInterpolatedValue(currentTime).Value;
125 }
126 }
127
128 private void CheckRouteValidity()
129 {
130 // TODO: Verify that the current route is one that can be played back (ie has valid time data)
131 // TODO: notify user that playback is impossible if the route does not have time data and throw exception
132 control = PlaybackAnimationRouteControlLayer.Instances[0].Control;
133 oldSelection = control.SelectedItems;
134 }
135
136 private static DateTime DateTimeMin(DateTime a, DateTime b)
137 {
138 return a < b ? a : b;
139 }
140
141 private static DateTime DateTimeMax(DateTime a, DateTime b)
142 {
143 return a > b ? a : b;
144 }
145
146 private static int RouteIndexFromDistance(IGPSRoute route, double distance)
147 {
148 // FIXME: add out parameter to output the remaining distance to improve accuracy
149 IEnumerator<ITimeValueEntry<IGPSPoint>> i = route.GetEnumerator();
150 int index = 0;
151 double count = 0.0;
152 i.MoveNext();
153 IGPSPoint prev = i.Current.Value;
154 while (i.MoveNext())
155 {
156 IGPSPoint cur = i.Current.Value;
157 count += prev.DistanceMetersToPoint(cur);
158 if (count < distance)
159 {
160 index++;
161 prev = cur;
162 }
163 else
164 {
165 return index;
166 }
167 }
168 return index;
169 }
170
171 private static DateTime RouteTimeFromDistance(IGPSRoute route, double distance)
172 {
173 int index = RouteIndexFromDistance(route, distance);
174 return route.StartTime.AddSeconds(route[index].ElapsedSeconds);
175 }
176
177 private void CalculateSelectedRegion(out DateTime start, out DateTime end)
178 {
179 // FIXME: account for multiple ranges within one selection
180 start = DateTime.MaxValue;
181 end = DateTime.MinValue;
182 for (int k = 0; k < control.SelectedItems.Count; k++)
183 {
184 IRouteControlSelection selection = control.SelectedItems[k];
185 if (selection == null || !selection.Item.DisplayRoute || selection.Item.Item == null)
186 {
187 return;
188 }
189 if (selection.DistanceMetersRanges != null && selection.DistanceMetersRanges.Count >= 1)
190 {
191 start = DateTimeMin(start, RouteTimeFromDistance(route, selection.DistanceMetersRanges[0].Lower));
192 end = DateTimeMax(end, RouteTimeFromDistance(route, selection.DistanceMetersRanges[0].Upper));
193 }
194 else if (selection.TimeRanges != null && selection.TimeRanges.Count >= 1)
195 {
196 start = DateTimeMin(start, selection.TimeRanges[0].Lower);
197 end = DateTimeMax(start, selection.TimeRanges[0].Upper);
198 }
199 }
200 if (start == DateTime.MaxValue)
201 {
202 start = DateTime.MinValue;
203 }
204 if (end == DateTime.MinValue)
205 {
206 end = DateTime.MaxValue;
207 }
208 }
209
210 private void MarkSelectedRegion()
211 {
212 item = control.Items[0];
213 route = item.Item.GPSRoute;
214 IList<IRouteControlSelection> selection = PlaybackAnimationRouteControlLayer.Instances[0].Control.SelectedItems;
215 CalculateSelectedRegion(out startTime, out endTime);
216 if (endTime == startTime)
217 {
218 initialSelection = startTime;
219 startTime = DateTime.MinValue;
220 endTime = DateTime.MaxValue;
221 }
222 else
223 {
224 initialSelection = null;
225 }
226 startTime = DateTimeMax(startTime, route.StartTime);
227 if (startTime > route.StartTime)
228 {
229 MapMarker startMarker = PlaybackAnimationRouteControlLayer.Instances[0].StartMarker;
230 startMarker.Location = route.GetInterpolatedValue(startTime).Value;
231 startMarker.Visible = true;
232 }
233 endTime = DateTimeMin(endTime, route.StartTime.AddSeconds(route.TotalElapsedSeconds));
234 if (endTime < route.StartTime.AddSeconds(route.TotalElapsedSeconds))
235 {
236 MapMarker endMarker = PlaybackAnimationRouteControlLayer.Instances[0].EndMarker;
237 endMarker.Location = route.GetInterpolatedValue(endTime).Value;
238 endMarker.Visible = true;
239 }
240 PlaybackAnimationRouteControlLayer.Instances[0].UpdateMarkers();
241 CurrentTime = initialSelection == null ? startTime : initialSelection.Value;
242 }
243
244 public enum PlaybackState
245 {
246 Disabled,
247 Stopped,
248 Paused,
249 Playing,
250 FastForwarding,
251 Rewinding
252 }
253
254 private void playButton_Click(object sender, EventArgs e)
255 {
256 if (State == PlaybackState.Stopped && CurrentTime >= endTime)
257 {
258 CurrentTime = startTime;
259 }
260 State = State == PlaybackState.Playing || State == PlaybackState.FastForwarding || State == PlaybackState.Rewinding ? PlaybackState.Paused : PlaybackState.Playing;
261 }
262
263 private void fastForwardButton_Click(object sender, EventArgs e)
264 {
265 if (State == PlaybackState.Paused)
266 {
267 CurrentTime = CurrentTime.AddSeconds(STEP_SIZE);
268 }
269 else
270 {
271 // TODO: implement fast-forward
272 }
273 }
274
275 private void rewindButton_Click(object sender, EventArgs e)
276 {
277 if (State == PlaybackState.Paused)
278 {
279 CurrentTime = CurrentTime.AddSeconds(-STEP_SIZE);
280 }
281 else
282 {
283 // TODO: implement rewind
284 }
285 }
286
287 private void playbackBar_Scroll(object sender, EventArgs e)
288 {
289 CurrentTime = startTime.AddMilliseconds(playbackBar.Value * PLAYBACKBAR_TICK_GRANULARITY);
290 }
291
292 private void PlaybackTick(object source, ElapsedEventArgs args)
293 {
294 CurrentTime = CurrentTime.AddMilliseconds((double) speedSpinner.Value * timer.Interval);
295 if (CurrentTime >= endTime)
296 {
297 if (loopButton.Checked)
298 {
299 CurrentTime = startTime;
300 }
301 else
302 {
303 State = PlaybackState.Stopped;
304 }
305 }
306 }
307
308 private void control_SelectedItemsChanged(object sender, EventArgs e)
309 {
310 // FIXME: account for multiple ranges within one selection
311 DateTime selectedTime = DateTime.MinValue;
312 for (int k = 0; k < control.SelectedItems.Count; k++)
313 {
314 IRouteControlSelection selection = control.SelectedItems[k];
315 if (selection == null || !selection.Item.DisplayRoute || selection.Item.Item == null)
316 {
317 return;
318 }
319 if (selection.DistanceMetersRanges != null && selection.DistanceMetersRanges.Count >= 1)
320 {
321 selectedTime = RouteTimeFromDistance(route, selection.DistanceMetersRanges[0].Lower);
322 }
323 else if (selection.TimeRanges != null && selection.TimeRanges.Count >= 1)
324 {
325 selectedTime = selection.TimeRanges[0].Lower;
326 }
327 if (selectedTime != DateTime.MinValue)
328 {
329 break;
330 }
331 }
332 if (selectedTime == DateTime.MinValue)
333 {
334 return;
335 }
336 TimeSpan diff = currentTime < selectedTime ? selectedTime - currentTime : currentTime - selectedTime;
337 if (diff.TotalMilliseconds > MOVE_SELECTION_THRESHOLD)
338 {
339 CurrentTime = selectedTime;
340 }
341 }
342 }
343 }

Managed by UCC Webmasters ViewVC Help
Powered by ViewVC 1.1.26