25 % Properties that correspond to app components
26 properties (Access =
public)
27 TrajectoryVisualizationUIFigure matlab.ui.Figure
28 gridMain matlab.ui.container.GridLayout
29 gridSettingsZoom matlab.ui.container.GridLayout
30 gridSettings matlab.ui.container.GridLayout
31 gridSettingsCtrl matlab.ui.container.GridLayout
32 ZoomSlider matlab.ui.control.Slider
33 ZoomSliderLabel matlab.ui.control.Label
34 PlaySpeedSlider matlab.ui.control.Slider
35 PlaySpeedSliderLabel matlab.ui.control.Label
36 SettingsLabel matlab.ui.control.Label
37 axTrajZoom matlab.ui.control.UIAxes
38 gridControl matlab.ui.container.GridLayout
39 gridControlBtns matlab.ui.container.GridLayout
40 fldCurrTime matlab.ui.control.EditField
41 btnGoToOut matlab.ui.control.Button
42 gridPlay matlab.ui.container.GridLayout
43 btnPlayPause matlab.ui.control.StateButton
44 btnNextFrame matlab.ui.control.Button
45 btnPrevFrame matlab.ui.control.Button
46 btnGoToIn matlab.ui.control.Button
47 gridTimeSliders matlab.ui.container.GridLayout
48 sliderTime matlab.ui.control.Slider
49 sliderRange matlab.ui.control.RangeSlider
50 gridFooter matlab.ui.container.GridLayout
51 lblMoss matlab.ui.control.Label
52 imgLogo matlab.ui.control.Image
53 axTrajMain matlab.ui.control.UIAxes
57 properties (Access = private)
58 traj % trajectory table of the imported traj
object
59 fs % frequency of the trajectory
60 mainPosePatch % posePatch Object of the main Plot
61 zoomPosePatch % posePatch Object of the zoom Plot
62 playing = false %
boolean to check if the animation is playing
63 playTimer % timer for the animation
64 pointIn % in point of the trajectory
65 pointOut % out point of the trajectory
66 colors % global colormap
67 colorIdx % mapping from traj idx to color idx
71 % Callbacks that
handle component events
72 methods (Access = private)
74 % Code that executes after component creation
75 function startupFcn(app, traj)
83 % check traj TODO: add possibility to
import on startup, if
85 assert(height(traj.getTab) > 0,"There must exist Waypoint Entries in the Trajectory Object.")
87 traj.generateTrajectory;
90 app.traj = traj.getTrajectory;
91 app.fs = traj.getSamplingFrequency;
93 %% setup the play timer
94 fps = min([app.fs, 30]);
95 app.playTimer = timer(
"ExecutionMode",
"fixedRate",
"Period",round(1/fps,3),
"TimerFcn", {@app.playTimerCallback});
96 app.playTimer.UserData = 1;
98 %% generate the colormap
99 [~, idx, app.colorIdx] = unique([app.traj.pos,app.traj.ori.normalize.compact],
"rows",
"stable");
100 pos = app.traj.pos(idx,:);
101 app.colors = turbo(height(pos));
103 %% generate initial Main Plot
105 app.mainPosePatch = poseplot(app.axTrajMain,app.traj.ori(1),app.traj.pos(1,:),
"NED",
"MeshFileName",
"jumpSimplified.stl");
106 app.mainPosePatch.PatchFaceColor = app.colors(1,:);
107 app.mainPosePatch.PatchFaceAlpha = 1;
108 app.mainPosePatch.ScaleFactor = 5;
111 app.axTrajMain.XLim = [min(app.traj.pos(:,1)) - 0.5, max(app.traj.pos(:,1)) + 0.5];
112 app.axTrajMain.YLim = [min(app.traj.pos(:,2)) - 0.5, max(app.traj.pos(:,2)) + 0.5];
113 app.axTrajMain.ZLim = [min(app.traj.pos(:,3)) - 0.5, max(app.traj.pos(:,3)) + 0.5];
114 app.axTrajMain.Projection =
"perspective";
115 app.axTrajMain.XGrid =
"on";
116 app.axTrajMain.YGrid =
"on";
117 app.axTrajMain.ZGrid =
"on";
118 app.axTrajMain.Title.String =
"";
119 app.axTrajMain.Title.Visible =
"off";
120 app.axTrajMain.XLabel.String =
"x [m]";
121 app.axTrajMain.YLabel.String =
"y [m]";
122 app.axTrajMain.ZLabel.String =
"z [m]";
123 app.axTrajMain.PlotBoxAspectRatioMode =
"manual";
124 app.axTrajMain.PlotBoxAspectRatio = [1,1,1];
125 app.axTrajMain.DataAspectRatioMode =
"manual";
126 app.axTrajMain.DataAspectRatio = [1,1,1];
129 plotStep = ceil(height(pos)/1000);
130 for i=1:plotStep:height(pos)-plotStep-1
131 hold(app.axTrajMain,"on");
132 plot3(app.axTrajMain,pos([i,i+plotStep],1),pos([i,i+plotStep],2),pos([i,i+plotStep],3),
'-',
'Color',app.colors(i,:),
'LineWidth',0.5)
136 app.pointIn = plot3(app.axTrajMain,app.traj.pos(1,1),app.traj.pos(1,2),app.traj.pos(1,3),
'o',
'MarkerSize',5,
'MarkerFaceColor',app.colors(1,:),
'MarkerEdgeColor',[0,0.57,0.82]);
137 app.pointOut = plot3(app.axTrajMain,app.traj.pos(end,1),app.traj.pos(end,2),app.traj.pos(end,3),
'o',
'MarkerSize',5,
'MarkerFaceColor',app.colors(end,:),
'MarkerEdgeColor',[0.82,0.57,0]);
138 hold(app.axTrajMain,
"off");
140 %% generate initial Zoom Plot
142 app.zoomPosePatch = poseplot(app.axTrajZoom,app.traj.ori(1),app.traj.pos(1,:),
"NED",
"MeshFileName",
"jumpSimplified.stl");
143 app.zoomPosePatch.PatchFaceColor = app.colors(1,:);
144 app.zoomPosePatch.PatchFaceAlpha = 1;
145 app.zoomPosePatch.ScaleFactor = 2;
148 app.axTrajZoom.XLim = [app.traj.pos(1,1) - 0.5, app.traj.pos(1,1) + 0.5];
149 app.axTrajZoom.YLim = [app.traj.pos(1,2) - 0.5, app.traj.pos(1,2) + 0.5];
150 app.axTrajZoom.ZLim = [app.traj.pos(1,3) - 0.5, app.traj.pos(1,3) + 0.5];
151 app.axTrajZoom.Projection =
"perspective";
152 app.axTrajZoom.XGrid =
"on";
153 app.axTrajZoom.YGrid =
"on";
154 app.axTrajZoom.ZGrid =
"on";
155 app.axTrajZoom.Title.String =
"";
156 app.axTrajZoom.Title.Visible =
"off";
157 app.axTrajZoom.XLabel.String =
"x [m]";
158 app.axTrajZoom.YLabel.String =
"y [m]";
159 app.axTrajZoom.ZLabel.String =
"z [m]";
160 app.axTrajZoom.PlotBoxAspectRatioMode =
"manual";
161 app.axTrajZoom.PlotBoxAspectRatio = [1,1,1];
162 app.axTrajZoom.DataAspectRatioMode =
"manual";
163 app.axTrajZoom.DataAspectRatio = [1,1,1];
166 plotStep = ceil(height(pos)/1000);
167 for i=1:plotStep:height(pos)-plotStep-1
168 hold(app.axTrajZoom,"on");
169 plot3(app.axTrajZoom,pos([i,i+plotStep],1),pos([i,i+plotStep],2),pos([i,i+plotStep],3),
'-',
'Color',app.colors(i,:),
'LineWidth',2)
173 % set up time indicator
174 app.fldCurrTime.Value = string(app.traj.t(1),
"mm:ss.SSS");
177 app.sliderTime.Limits = seconds([app.traj.t(1),app.traj.t(end)]);
178 app.sliderTime.Value = seconds(app.traj.t(1));
179 app.sliderTime.ValueChangingFcn = createCallbackFcn(app, @sliderTimeChange,
true);
181 % set up range slider
182 app.sliderRange.Limits = seconds([app.traj.t(1),app.traj.t(end)]);
183 app.sliderRange.Value = seconds([app.traj.t(1),app.traj.t(end)]);
184 app.sliderRange.ValueChangingFcn = createCallbackFcn(app, @sliderRangeChanging,
true);
187 app.PlaySpeedSlider.Value = 1;
190 app.ZoomSlider.Value = 10;
191 app.ZoomSlider.ValueChangedFcn = createCallbackFcn(app, @zoomSliderChange,
false);
193 % set up play/pause button
194 app.btnPlayPause.ValueChangedFcn = createCallbackFcn(app, @playPause,
true);
196 % set up next/prev frame buttons
197 app.btnNextFrame.ButtonPushedFcn = createCallbackFcn(app, @nextFrame,
false);
198 app.btnPrevFrame.ButtonPushedFcn = createCallbackFcn(app, @prevFrame,
false);
200 % set up go to in/out buttons
201 app.btnGoToIn.ButtonPushedFcn = createCallbackFcn(app, @goToIn,
false);
202 app.btnGoToOut.ButtonPushedFcn = createCallbackFcn(app, @goToOut,
false);
204 % set up the current time field
205 app.fldCurrTime.Value = string(app.traj.t(1),
"mm:ss.SSS");
206 app.fldCurrTime.ValueChangedFcn = createCallbackFcn(app, @fldCurrTimeChanged,
false);
209 msg =
"There occured an error during start. Please check the following error message:" + newline + newline + e.message;
211 e = errordlg(msg,
"Startup Failed",
"modal");
219 % Value changed function: sliderTime
220 function sliderTimeChange(app, event)
222 % find the closest index to the time
223 [~,idx] = min(abs(app.traj.t - seconds(event.Value)));
226 app.playPause(play=
false);
228 % wait
for the animation to stop
233 app.updatePlots(idx, updateTimeSlider=
true);
234 app.playTimer.UserData = idx;
237 % Value changed function: btnPlayPause
238 function playPause(app,event,options)
243 options.play (1,1) logical =
true
246 if isa(event,
'matlab.ui.eventdata.ValueChangedData')
247 app.playing = event.Value;
249 app.playing = options.play;
254 app.btnPlayPause.Icon = "Play.svg";
258 app.btnPlayPause.Icon = "Pause.svg";
259 app.playTimer.UserData = find(app.traj.t >= seconds(app.sliderTime.Value),1);
260 start(app.playTimer);
264 % value changed function: zoomSlider
265 function zoomSliderChange(app)
266 idx = app.playTimer.UserData;
267 app.axTrajZoom.XLim = [app.traj.pos(idx,1) - 1/app.ZoomSlider.Value, app.traj.pos(idx,1) + 1/app.ZoomSlider.Value];
268 app.axTrajZoom.YLim = [app.traj.pos(idx,2) - 1/app.ZoomSlider.Value, app.traj.pos(idx,2) + 1/app.ZoomSlider.Value];
269 app.axTrajZoom.ZLim = [app.traj.pos(idx,3) - 1/app.ZoomSlider.Value, app.traj.pos(idx,3) + 1/app.ZoomSlider.Value];
272 % Button pushed function: btnNextFrame
273 function nextFrame(app)
275 app.playPause(play=false);
277 % wait for the animation to stop
283 idx = app.playTimer.UserData + 1;
284 if idx > height(app.traj)
285 idx = height(app.traj);
287 app.updatePlots(idx);
288 app.playTimer.UserData = idx;
291 % Button pushed function: btnPrevFrame
292 function prevFrame(app)
294 app.playPause(play=false);
296 % wait for the animation to stop
302 idx = app.playTimer.UserData - 1;
306 app.updatePlots(idx);
307 app.playTimer.UserData = idx;
310 % Button pushed function: btnGoToIn
313 app.playPause(play=false);
315 % wait for the animation to stop
320 % find the closest index to the first value of the range slider
321 [~,idx] = min(abs(app.traj.t - seconds(app.sliderRange.Value(1))));
322 app.updatePlots(idx);
323 app.playTimer.UserData = idx;
326 % Button pushed function: btnGoToOut
327 function goToOut(app)
329 app.playPause(play=false);
331 % wait for the animation to stop
336 % find the closest index to the second value of the range slider
337 [~,idx] = min(abs(app.traj.t - seconds(app.sliderRange.Value(2))));
338 app.updatePlots(idx);
339 app.playTimer.UserData = idx;
342 % Value changed function: fldCurrTime
343 function fldCurrTimeChanged(app, varargin)
345 app.playPause(play=false);
347 % wait for the animation to stop
353 if contains(app.fldCurrTime.Value,":")
354 time = datetime(app.fldCurrTime.Value,"InputFormat","mm:ss.SSS");
356 time = seconds(str2double(app.fldCurrTime.Value));
359 % find the closest index to the time
360 [~,idx] = min(abs(app.traj.t - time));
362 app.updatePlots(idx, updateTimeSlider=true);
363 app.playTimer.UserData = idx;
366 % Value changing function: sliderRange
367 function sliderRangeChanging(app, event)
370 [~,idx1] = min(abs(app.traj.t - seconds(event.Value(1))));
371 [~,idx2] = min(abs(app.traj.t - seconds(event.Value(2))));
373 % update the in point
374 app.pointIn.XData = app.traj.pos(idx1,1);
375 app.pointIn.YData = app.traj.pos(idx1,2);
376 app.pointIn.ZData = app.traj.pos(idx1,3);
377 app.pointIn.MarkerFaceColor = app.colors(app.colorIdx(idx1),:);
378 app.pointIn.MarkerEdgeColor = app.colors(app.colorIdx(idx1),:);
380 % update the out point
381 app.pointOut.XData = app.traj.pos(idx2,1);
382 app.pointOut.YData = app.traj.pos(idx2,2);
383 app.pointOut.ZData = app.traj.pos(idx2,3);
384 app.pointOut.MarkerFaceColor = app.colors(app.colorIdx(idx2),:);
385 app.pointOut.MarkerEdgeColor = app.colors(app.colorIdx(idx2),:);
391 methods (Access = private)
392 function updatePlots(app, idx, options)
395 idx (1,1) {mustBeInteger, mustBePositive}
396 options.updateMain (1,1) logical =
true
397 options.updateZoom (1,1) logical =
true
398 options.updateCurrTime (1,1) logical =
true
399 options.updateTimeSlider (1,1) logical =
true
402 % update the controls
403 if options.updateCurrTime
404 app.fldCurrTime.Value = string(app.traj.t(idx),
"mm:ss.SSS");
406 if options.updateTimeSlider
407 app.sliderTime.Value = seconds(app.traj.t(idx));
410 % check
if position or orientation has changed
411 if options.updateMain || options.updateZoom
412 if any(app.mainPosePatch.Position ~= app.traj.pos(idx,:)) || any(app.zoomPosePatch.Position ~= app.traj.pos(idx,:))
417 if any(app.mainPosePatch.Orientation ~= app.traj.ori(idx,:)) || any(app.zoomPosePatch.Orientation ~= app.traj.ori(idx,:))
424 % update the main plot
if different
425 if options.updateMain
427 app.mainPosePatch.Position = app.traj.pos(idx,:);
428 app.mainPosePatch.PatchFaceColor = app.colors(app.colorIdx(idx),:);
431 app.mainPosePatch.Orientation = app.traj.ori(idx,:);
435 % update the zoom plot
if different
436 if options.updateZoom
438 app.zoomPosePatch.Position = app.traj.pos(idx,:);
439 app.zoomPosePatch.PatchFaceColor = app.colors(app.colorIdx(idx),:);
441 app.axTrajZoom.XLim = [app.traj.pos(idx,1) - 1/app.ZoomSlider.Value, app.traj.pos(idx,1) + 1/app.ZoomSlider.Value];
442 app.axTrajZoom.YLim = [app.traj.pos(idx,2) - 1/app.ZoomSlider.Value, app.traj.pos(idx,2) + 1/app.ZoomSlider.Value];
443 app.axTrajZoom.ZLim = [app.traj.pos(idx,3) - 1/app.ZoomSlider.Value, app.traj.pos(idx,3) + 1/app.ZoomSlider.Value];
446 app.zoomPosePatch.Orientation = app.traj.ori(idx,:);
451 function playTimerCallback(app, ~, ~)
452 % check
if animation et end or before of range slider
453 idx_range_start = find(app.traj.t >= seconds(app.sliderRange.Value(1)),1);
454 idx_range_end = find(app.traj.t >= seconds(app.sliderRange.Value(2)),1);
455 if app.playTimer.UserData > idx_range_end || app.playTimer.UserData < idx_range_start
456 % jump to start of range slider
457 app.playTimer.UserData = idx_range_start;
461 app.updatePlots(app.playTimer.UserData);
464 curr_fps = 1/(app.playTimer.InstantPeriod);
469 % calculate steps to skip to keep up with the desired frequency
470 skip = ceil(app.PlaySpeedSlider.Value * app.fs/curr_fps);
474 app.playTimer.UserData = app.playTimer.UserData + skip;
479 % Component initialization
480 methods (Access =
private)
482 % Create UIFigure and components
483 function createComponents(app)
485 % Create TrajectoryVisualizationUIFigure and hide until all components are created
486 app.TrajectoryVisualizationUIFigure = uifigure(
'Visible',
'off');
487 app.TrajectoryVisualizationUIFigure.Position = [100 100 1024 768];
488 app.TrajectoryVisualizationUIFigure.Name =
'Trajectory Visualization';
489 app.TrajectoryVisualizationUIFigure.Icon =
'logo_zhaw.png';
490 app.TrajectoryVisualizationUIFigure.CloseRequestFcn = createCallbackFcn(app, @
delete,
false);
493 app.gridMain = uigridlayout(app.TrajectoryVisualizationUIFigure);
494 app.gridMain.ColumnWidth = {
'1x'};
495 app.gridMain.RowHeight = {
'3x',
'2x', 70, 50};
496 app.gridMain.Padding = [25 25 25 25];
499 app.axTrajMain = uiaxes(app.gridMain);
500 title(app.axTrajMain,
'Title')
501 xlabel(app.axTrajMain, 'X')
502 ylabel(app.axTrajMain, 'Y')
503 zlabel(app.axTrajMain, 'Z')
504 app.axTrajMain.Layout.Row = 1;
505 app.axTrajMain.Layout.Column = 1;
508 app.gridFooter = uigridlayout(app.gridMain);
509 app.gridFooter.RowHeight = {
'1x'};
510 app.gridFooter.ColumnSpacing = 0;
511 app.gridFooter.RowSpacing = 0;
512 app.gridFooter.Padding = [0 0 0 0];
513 app.gridFooter.Layout.Row = 4;
514 app.gridFooter.Layout.Column = 1;
517 app.imgLogo = uiimage(app.gridFooter);
518 app.imgLogo.Layout.Row = 1;
519 app.imgLogo.Layout.Column = 1;
520 app.imgLogo.HorizontalAlignment =
'left';
521 app.imgLogo.VerticalAlignment =
'bottom';
522 app.imgLogo.ImageSource =
'logo_isc.png';
525 app.lblMoss = uilabel(app.gridFooter);
526 app.lblMoss.HorizontalAlignment =
'right';
527 app.lblMoss.VerticalAlignment =
'bottom';
528 app.lblMoss.FontColor = [0.502 0.502 0.502];
529 app.lblMoss.Layout.Row = 1;
530 app.lblMoss.Layout.Column = 2;
531 app.lblMoss.Text =
'moss, February 2024';
534 app.gridControl = uigridlayout(app.gridMain);
535 app.gridControl.ColumnWidth = {300,
'2x'};
536 app.gridControl.RowHeight = {
'1x'};
537 app.gridControl.Layout.Row = 3;
538 app.gridControl.Layout.Column = 1;
540 % Create gridTimeSliders
541 app.gridTimeSliders = uigridlayout(app.gridControl);
542 app.gridTimeSliders.ColumnWidth = {
'fit',
'1x'};
543 app.gridTimeSliders.ColumnSpacing = 0;
544 app.gridTimeSliders.RowSpacing = 0;
545 app.gridTimeSliders.Padding = [0 0 0 0];
546 app.gridTimeSliders.Layout.Row = 1;
547 app.gridTimeSliders.Layout.Column = 2;
550 app.sliderRange = uislider(app.gridTimeSliders,
'range');
551 app.sliderRange.MajorTicks = [];
552 app.sliderRange.MajorTickLabels = {
''};
553 app.sliderRange.MinorTicks = [];
554 app.sliderRange.Layout.Row = 1;
555 app.sliderRange.Layout.Column = 2;
558 app.sliderTime = uislider(app.gridTimeSliders);
559 app.sliderTime.MajorTicks = [];
560 app.sliderTime.MajorTickLabels = {
''};
561 app.sliderTime.MinorTicks = [];
562 app.sliderTime.Layout.Row = 2;
563 app.sliderTime.Layout.Column = 2;
565 % Create gridControlBtns
566 app.gridControlBtns = uigridlayout(app.gridControl);
567 app.gridControlBtns.ColumnWidth = {
'1x',
'3x',
'1x',
'fit',
'2x'};
568 app.gridControlBtns.RowHeight = {
'1x'};
569 app.gridControlBtns.Padding = [0 10 0 10];
570 app.gridControlBtns.Layout.Row = 1;
571 app.gridControlBtns.Layout.Column = 1;
574 app.btnGoToIn = uibutton(app.gridControlBtns,
'push');
575 app.btnGoToIn.Icon =
'goToIn.svg';
576 app.btnGoToIn.Layout.Row = 1;
577 app.btnGoToIn.Layout.Column = 1;
578 app.btnGoToIn.Text =
'';
581 app.gridPlay = uigridlayout(app.gridControlBtns);
582 app.gridPlay.ColumnWidth = {
'1x',
'1x',
'1x'};
583 app.gridPlay.RowHeight = {
'1x'};
584 app.gridPlay.ColumnSpacing = 0;
585 app.gridPlay.RowSpacing = 0;
586 app.gridPlay.Padding = [0 0 0 0];
587 app.gridPlay.Layout.Row = 1;
588 app.gridPlay.Layout.Column = 2;
590 % Create btnPrevFrame
591 app.btnPrevFrame = uibutton(app.gridPlay,
'push');
592 app.btnPrevFrame.Icon =
'prevFrame.svg';
593 app.btnPrevFrame.Layout.Row = 1;
594 app.btnPrevFrame.Layout.Column = 1;
595 app.btnPrevFrame.Text =
'';
597 % Create btnNextFrame
598 app.btnNextFrame = uibutton(app.gridPlay,
'push');
599 app.btnNextFrame.Icon =
'nextFrame.svg';
600 app.btnNextFrame.Layout.Row = 1;
601 app.btnNextFrame.Layout.Column = 3;
602 app.btnNextFrame.Text =
'';
604 % Create btnPlayPause
605 app.btnPlayPause = uibutton(app.gridPlay,
'state');
606 app.btnPlayPause.Icon =
'Play.svg';
607 app.btnPlayPause.Text =
'';
608 app.btnPlayPause.Layout.Row = 1;
609 app.btnPlayPause.Layout.Column = 2;
612 app.btnGoToOut = uibutton(app.gridControlBtns,
'push');
613 app.btnGoToOut.Icon =
'goToOut.svg';
614 app.btnGoToOut.Layout.Row = 1;
615 app.btnGoToOut.Layout.Column = 3;
616 app.btnGoToOut.Text =
'';
619 app.fldCurrTime = uieditfield(app.gridControlBtns,
'text');
620 app.fldCurrTime.Layout.Row = 1;
621 app.fldCurrTime.Layout.Column = 5;
622 app.fldCurrTime.Value =
'00:00.000';
623 app.fldCurrTime.HorizontalAlignment =
'center';
625 % Create gridSettingsZoom
626 app.gridSettingsZoom = uigridlayout(app.gridMain);
627 app.gridSettingsZoom.ColumnWidth = {300,
'1x'};
628 app.gridSettingsZoom.RowHeight = {
'1x'};
629 app.gridSettingsZoom.Padding = [0 0 0 0];
630 app.gridSettingsZoom.Layout.Row = 2;
631 app.gridSettingsZoom.Layout.Column = 1;
634 app.axTrajZoom = uiaxes(app.gridSettingsZoom);
635 title(app.axTrajZoom,
'Title')
636 xlabel(app.axTrajZoom, 'X')
637 ylabel(app.axTrajZoom, 'Y')
638 zlabel(app.axTrajZoom, 'Z')
639 app.axTrajZoom.Layout.Row = 1;
640 app.axTrajZoom.Layout.Column = 2;
642 % Create gridSettings
643 app.gridSettings = uigridlayout(app.gridSettingsZoom);
644 app.gridSettings.ColumnWidth = {
'1x'};
645 app.gridSettings.RowHeight = {
'fit',
'1x'};
646 app.gridSettings.Padding = [0 0 0 0];
647 app.gridSettings.Layout.Row = 1;
648 app.gridSettings.Layout.Column = 1;
650 % Create SettingsLabel
651 app.SettingsLabel = uilabel(app.gridSettings);
652 app.SettingsLabel.FontSize = 14;
653 app.SettingsLabel.FontWeight =
'bold';
654 app.SettingsLabel.Layout.Row = 1;
655 app.SettingsLabel.Layout.Column = 1;
656 app.SettingsLabel.Text =
'Settings';
658 % Create gridSettingsCtrl
659 app.gridSettingsCtrl = uigridlayout(app.gridSettings);
660 app.gridSettingsCtrl.ColumnWidth = {
'fit',
'1x'};
661 app.gridSettingsCtrl.RowHeight = {
'fit',
'fit',
'fit'};
662 app.gridSettingsCtrl.Padding = [0 0 0 0];
663 app.gridSettingsCtrl.Layout.Row = 2;
664 app.gridSettingsCtrl.Layout.Column = 1;
666 % Create PlaySpeedSliderLabel
667 app.PlaySpeedSliderLabel = uilabel(app.gridSettingsCtrl);
668 app.PlaySpeedSliderLabel.HorizontalAlignment =
'right';
669 app.PlaySpeedSliderLabel.Layout.Row = 1;
670 app.PlaySpeedSliderLabel.Layout.Column = 1;
671 app.PlaySpeedSliderLabel.Text =
'Play Speed';
673 % Create PlaySpeedSlider
674 app.PlaySpeedSlider = uislider(app.gridSettingsCtrl);
675 app.PlaySpeedSlider.Limits = [0 5];
676 app.PlaySpeedSlider.Layout.Row = 1;
677 app.PlaySpeedSlider.Layout.Column = 2;
679 % Create ZoomSliderLabel
680 app.ZoomSliderLabel = uilabel(app.gridSettingsCtrl);
681 app.ZoomSliderLabel.HorizontalAlignment =
'right';
682 app.ZoomSliderLabel.Layout.Row = 2;
683 app.ZoomSliderLabel.Layout.Column = 1;
684 app.ZoomSliderLabel.Text =
'Zoom';
687 app.ZoomSlider = uislider(app.gridSettingsCtrl);
688 app.ZoomSlider.Limits = [2 22];
689 app.ZoomSlider.Layout.Row = 2;
690 app.ZoomSlider.Layout.Column = 2;
692 % Show the figure after all components are created
693 app.TrajectoryVisualizationUIFigure.Visible =
'on';
697 % App creation and deletion
698 methods (Access =
public)
703 % Create UIFigure and components
704 createComponents(app)
706 % Register the app with App Designer
707 registerApp(app, app.TrajectoryVisualizationUIFigure)
709 % Execute the startup function
710 runStartupFcn(app, @(app)startupFcn(app, varargin{:}))
717 % Code that executes before app deletion
720 % make sure the timer is stopped
723 delete(app.playTimer);
727 % Delete UIFigure when app is deleted
728 delete(app.TrajectoryVisualizationUIFigure)