-
Notifications
You must be signed in to change notification settings - Fork 0
/
MotorSilhouette.m
305 lines (274 loc) · 13.5 KB
/
MotorSilhouette.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Core Model, 2022
% Written by Maya Davis
% Concept by Maya Davis and Melissa A. Redford
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% METHOD LIST
%
% MotorSilhouette
%
% DROPOFF FUNCTIONS FOR LOOKBACK & LOOKAHEAD WINDOWS
% DropoffScalar
% DropoffScalarConcave
%
% EXPAND MOTOR SILHOUETTE
% ExpandSilhouette
%
% PLOTTING INFO AND PLOTTING
% TemporalActivationInfo
% PlottingInfo3D
% PlottingInfo
% Plot
%% CLASS DEFINITION
classdef MotorSilhouette
% A motor silhouette consists of its timestamps & region at each time
%% PROPERTIES
properties
Regions;
end
%% METHODS
methods
% Creating an object
function obj = MotorSilhouette(MotorRegions)
obj.Regions = MotorRegions;
end
%% DROPOFF FUNCTIONS FOR LOOKBACK & LOOKAHEAD WINDOWS
% DropoffScalar
% DropoffScalarConcave
% Takes a time distance as an input, and gives as an output a
% scalar that represents the weight that should be given to the
% part of the motor silhouette that is that time distance away from
% the present.
% Default dropoff function -- references one of the ones below
function ScalarMultiplier = DropoffScalar(obj, TimeDistance, ...
LookAheadWindow, LookBackWindow)
ScalarMultiplier = obj.DropoffScalarConcave(TimeDistance, ...
LookAheadWindow, LookBackWindow, 2);
end
% Drops off concavely, i.e. faster at first and then slower. The
% higher Power is, the closer the dropoff is to just a step
% function that goes from 1 to 0. If Power = 1, then it just drops
% off linearly.
function ScalarMultiplier = DropoffScalarConcave(~, ...
TimeDistance, LookAheadWindow, LookBackWindow, Power)
% Adding one to these first is the easiest way to run this
% algorithm
LookAheadWindow = LookAheadWindow + 1;
LookBackWindow = LookBackWindow + 1;
% TimeDistance is positive if we're looking forward in time and
% negative if we're looking backward in time
if TimeDistance > -LookBackWindow
if TimeDistance > 0
if TimeDistance < LookAheadWindow
% TimeDistance is between 0 and LookAheadWindow
ScalarMultiplier = ...
(1 - (TimeDistance/LookAheadWindow))^Power;
else
% TimeDistance is greater than or equal to
% LookAheadWindow
ScalarMultiplier = 0;
end
else
% TimeDistance is between -LookBackWindow and 0
ScalarMultiplier = ...
(1 - (-TimeDistance/LookBackWindow))^Power;
end
else
% TimeDistance is less than or equal to -LookBackWindow
ScalarMultiplier = 0;
end
end
%% EXPAND MOTOR SILHOUETTE
% ExpandSilhouette
function ExpandedSilhouette = ExpandSilhouette(obj, motorTrajectory)
% For now, the motorTrajectory has to have the same length as
% the silhouette
assert(size(motorTrajectory.CoordinateMatrix, 2) == length( ...
obj.Regions), "The motor silhouette is " + ...
length(obj.Regions) + " time steps long but the " + ...
"trajectory is " + ...
size(motorTrajectory.CoordinateMatrix, 2) + ...
" time steps long");
NewRegions = WeightedMotorSimplicialComplex.empty(0, length(obj.Regions));
for t = 1:length(obj.Regions)
NewRegion = obj.Regions(t).Expand(motorTrajectory.CoordinateMatrix(:, t));
NewRegions(t) = NewRegion;
end
ExpandedSilhouette = MotorSilhouette(NewRegions);
end
%% PLOTTING INFO AND PLOTTING
% TemporalActivationInfo
% PlottingInfo3D
% PlottingInfo
% Plot
% Time-varying activation info: the output, ActivationValuesCell is
% a cell array such that ActivationValuesCell{t,1} will be
% something like [0; 0.5; 1; 0.5; 0; 0], which would mean that at
% time t, the motor silhouette is fully active at the third region,
% half active at the 2nd and 4th regions, and not active at the
% 1st, 5th, and 6th regions. This activation would be determined
% by the dropoff function.
function ActivationValuesMatrix = ...
TemporalActivationInfo(obj, LookBackAmt, LookAheadAmt)
% Inititalize ActivationValuesCell to the right size -- the
% number of time steps.
ActivationValuesMatrix = nan(length(obj.Regions), length(obj.Regions));
for t = 1:size(ActivationValuesMatrix, 1)
% Find the span of the window for which the activation is
% nonzero -- will be a little tricky at the beginning and
% end -- a is the beginning of the window and b is the end
a = max(1, t - LookBackAmt);
b = min(length(ActivationValuesMatrix), t + LookAheadAmt);
% Initialize the set of activation values that will go in
% these units in the cells; t is the index of the main
% region (maximal activation, unless we're using an unusual
% dropoff function), and then we'll go from t - LookBackAmt
% to t + LookAheadAmt, but making sure not to go past the
% end of the silhouette on either side. Note: we'll have
% zeros for activation values outside of the range a to b
CurrentActivationValues = zeros(length(obj.Regions),1);
% Fill in the values according to the DropoffScalar
% function
for RegionIndex = a:b
Distance = RegionIndex - t;
Activation = obj.DropoffScalar(Distance, ...
LookAheadAmt, LookBackAmt);
CurrentActivationValues(RegionIndex, 1) = Activation;
end
ActivationValuesMatrix(t,:) = CurrentActivationValues;
end
end
% Vertex and face data for plotting the silhouette using the patch
% function -- this will be the plotting info for plotting it as a
% sequence of polygons, with the number of vertices specified as an
% input.
% EXAMPLE: If we want to plot a triangle with vertices (1,1) (2,2)
% and (2,1) and a square with vertices (3,3) (4,3) (4,4) and (3,4),
% then we want:
% VertexData = [1 1; 2 2; 2 1; 3 3; 4 3; 4 4; 3 4]
% FaceData = [1 2 3 NaN; 4 5 6 7]
function [VertexData, FaceData, AlphaData] = PlottingInfo3D(obj, AlphaMin, AlphaMax)
TotalNumVertices = 0;
TotalNumFaceRows = 0;
MaxNumVertices = 0;
for t = 1:length(obj.Regions)
CurrentNumVertices = size(obj.Regions(t).MotorVertexList, 1);
TotalNumVertices = TotalNumVertices + CurrentNumVertices;
MaxNumVertices = max(MaxNumVertices, CurrentNumVertices);
CurrentNumFaceRows = size(obj.Regions(t).SimplexMatrix, 1);
TotalNumFaceRows = TotalNumFaceRows + CurrentNumFaceRows;
end
NumCoordinates = size(obj.Regions(1).MotorVertexList, 2);
% Initializing outputs to be the right sizes
VertexData = zeros(TotalNumVertices, NumCoordinates);
FaceData = nan(TotalNumFaceRows, MaxNumVertices);
AlphaData = zeros(TotalNumFaceRows, 1);
% Starting filling in outputs
StartingVertexIndex = 1;
StartingFaceIndex = 1;
NumVerticesSoFar = 0;
for t = 1:length(obj.Regions)
[CurrentFaceData, CurrentVertexData, CurrentAlphaData] = ...
obj.Regions(t).PlottingInfo("AlphaMin", AlphaMin, "AlphaMax", AlphaMax);
% Vertex stuff
CurrentNumVertices = size(CurrentVertexData, 1);
EndingVertexIndex = StartingVertexIndex + CurrentNumVertices - 1;
VertexData(StartingVertexIndex:EndingVertexIndex,:) = CurrentVertexData;
% Face stuff
ModifiedFaceData = CurrentFaceData + NumVerticesSoFar;
CurrentNumFaceRows = size(ModifiedFaceData, 1);
EndingFaceIndex = StartingFaceIndex + CurrentNumFaceRows - 1;
FaceData(StartingFaceIndex:EndingFaceIndex, 1:size(ModifiedFaceData, 2)) = ModifiedFaceData;
% Alpha stuff (based on face indices)
AlphaData(StartingFaceIndex:EndingFaceIndex) = CurrentAlphaData;
% Incrementing indices
NumVerticesSoFar = NumVerticesSoFar + CurrentNumVertices;
StartingVertexIndex = EndingVertexIndex + 1;
StartingFaceIndex = EndingFaceIndex + 1;
end
end
% Vertex and face data for plotting the silhouette using the patch
% function -- this will be the plotting info for plotting it as a
% sequence of polygons, with the number of vertices specified as an
% input.
% EXAMPLE: If we want to plot a triangle with vertices (1,1) (2,2)
% and (2,1) and a square with vertices (3,3) (4,3) (4,4) and (3,4),
% then we want:
% VertexData = [1 1; 2 2; 2 1; 3 3; 4 3; 4 4; 3 4]
% FaceData = [1 2 3 NaN; 4 5 6 7]
function [VertexData, FaceData, AlphaData] = PlottingInfo(obj, AlphaMin, AlphaMax)
% Finding necessary sizes of things
TotalNumVertices = 0;
TotalNumFaceRows = 0;
MaxNumVertices = 0;
for t = 1:length(obj.Regions)
CurrentNumVertices = size(obj.Regions(t).MotorVertexList, 1);
TotalNumVertices = TotalNumVertices + CurrentNumVertices;
MaxNumVertices = max(MaxNumVertices, CurrentNumVertices);
CurrentNumFaceRows = size(obj.Regions(t).SimplexMatrix, 1);
TotalNumFaceRows = TotalNumFaceRows + CurrentNumFaceRows;
end
NumCoordinates = size(obj.Regions(1).MotorVertexList, 2);
% Initializing outputs to be the right sizes
VertexData = zeros(TotalNumVertices, NumCoordinates);
FaceData = nan(TotalNumFaceRows, MaxNumVertices);
AlphaData = zeros(TotalNumFaceRows, 1);
% Starting filling in outputs
StartingVertexIndex = 1;
StartingFaceIndex = 1;
NumVerticesSoFar = 0;
for t = 1:length(obj.Regions)
[CurrentFaceData, CurrentVertexData, CurrentAlphaData] = ...
obj.Regions(t).PlottingInfo("AlphaMin", AlphaMin, "AlphaMax", AlphaMax);
% Vertex stuff
CurrentNumVertices = size(CurrentVertexData, 1);
EndingVertexIndex = StartingVertexIndex + CurrentNumVertices - 1;
VertexData(StartingVertexIndex:EndingVertexIndex,:) = CurrentVertexData;
% Face stuff
ModifiedFaceData = CurrentFaceData + NumVerticesSoFar;
CurrentNumFaceRows = size(ModifiedFaceData, 1);
EndingFaceIndex = StartingFaceIndex + CurrentNumFaceRows - 1;
FaceData(StartingFaceIndex:EndingFaceIndex, 1:size(ModifiedFaceData, 2)) = ModifiedFaceData;
% Alpha stuff (based on face indices)
AlphaData(StartingFaceIndex:EndingFaceIndex) = CurrentAlphaData;
% Incrementing indices
NumVerticesSoFar = NumVerticesSoFar + CurrentNumVertices;
StartingVertexIndex = EndingVertexIndex + 1;
StartingFaceIndex = EndingFaceIndex + 1;
end
end
% Plotting the whole silhouette with full opacity, on axes (input),
% as a series of polygons with NumberOfVertices vertices (input),
% the color given by ColorRBG (input)
% FIX
function WholeSilhouettePlot = Plot(obj, axes, ColorRBG)
% Plots the silhouette
% Expand ColorRBG into a larger array that is the appropriate
% length
ColorArray = zeros(length(obj.Regions),3);
for t = 1:length(obj.Regions)
ColorArray(t,:) = ColorRBG;
end
AlphaMin = 0.3;
AlphaMax = 1;
% Vertex & Face Data
[VertexData, FaceData, AlphaData] = obj.PlottingInfo(AlphaMin, AlphaMax);
% AlphaArray are the opacity values, which are all 1 (fully
% opaque) in this case
AlphaArray = ones(length(obj.Regions),1);
% Plotting
hold(axes, "on");
for t = 1:length(VertexData)
CurrentVertexData = VertexData(t);
CurrentFaceData = FaceData(t);
WholeSilhouettePlot = patch(axes, ...
"Vertices", CurrentVertexData, ...
"Faces", CurrentFaceData, ...
"FaceVertexCData", ColorArray, ...
"FaceColor", "flat", "EdgeColor", "none", ...
"FaceVertexAlphaData", AlphaArray, "FaceAlpha", ...
"flat", "AlphaDataMapping", "none");
end
end
end
end