lexgui 0.7.11 → 0.7.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/extensions/timeline.js +206 -110
- package/build/lexgui.css +0 -1
- package/build/lexgui.js +50 -15
- package/build/lexgui.min.css +1 -1
- package/build/lexgui.min.js +1 -1
- package/build/lexgui.module.js +50 -15
- package/build/lexgui.module.min.js +1 -1
- package/changelog.md +14 -1
- package/package.json +1 -1
|
@@ -17,8 +17,8 @@ LX.registerIcon("TimelineLockOpen", '<svg xmlns="http://www.w3.org/2000/svg" wid
|
|
|
17
17
|
class Timeline {
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* @param {
|
|
21
|
-
* @param {
|
|
20
|
+
* @param {String} name = string unique id
|
|
21
|
+
* @param {Object} options = {skipLock, skipVisibility}
|
|
22
22
|
*/
|
|
23
23
|
constructor( id, options = {} ) {
|
|
24
24
|
|
|
@@ -104,6 +104,7 @@ class Timeline {
|
|
|
104
104
|
this.canvasArea = right;
|
|
105
105
|
this.canvasArea.root.classList.add("lextimelinearea");
|
|
106
106
|
|
|
107
|
+
this.selectedTracks = []; // [track, track] contains selected (highlighted) tracks. That is, tracks with .isSelected == true. Elements in array are not ordered. Only visible tracks should be selected
|
|
107
108
|
this.selectedItems = []; // [trackInfo, "groupId"], contains the visible items (tracks or groups) of the timeline
|
|
108
109
|
this.leftPanel = left.addPanel( { className: 'lextimelinepanel', width: "100%", height: "100%" } );
|
|
109
110
|
this.trackTreesPanel = null;
|
|
@@ -183,7 +184,6 @@ class Timeline {
|
|
|
183
184
|
|
|
184
185
|
/**
|
|
185
186
|
* @method updateHeader
|
|
186
|
-
* @param {*}
|
|
187
187
|
*/
|
|
188
188
|
|
|
189
189
|
updateHeader() {
|
|
@@ -315,8 +315,7 @@ class Timeline {
|
|
|
315
315
|
if ( this.onAddNewTrackButton ){
|
|
316
316
|
this.onAddNewTrackButton();
|
|
317
317
|
}else{
|
|
318
|
-
|
|
319
|
-
this.changeSelectedItems( [trackIdx] );
|
|
318
|
+
this.addNewTrack();
|
|
320
319
|
}
|
|
321
320
|
}, { hideName: true, title: "Add Track", icon: "Plus" });
|
|
322
321
|
}
|
|
@@ -335,13 +334,17 @@ class Timeline {
|
|
|
335
334
|
this.trackTreesComponent = p.addTree(null, treeTracks, {filter: false, rename: false, draggable: false, onevent: (e) => {
|
|
336
335
|
switch(e.type) {
|
|
337
336
|
case LX.TreeEvent.NODE_SELECTED:
|
|
337
|
+
if ( !e.event.shiftKey ){
|
|
338
|
+
this.deselectAllTracks( false ); // no need to update left panel
|
|
339
|
+
}
|
|
338
340
|
if (e.node.trackData){
|
|
339
|
-
|
|
341
|
+
const flag = e.event.shiftKey? !e.node.trackData.isSelected : true;
|
|
342
|
+
this.setTrackSelection( e.node.trackData.trackIdx, flag, false, false ); // do callback, do not update left panel
|
|
340
343
|
}
|
|
341
344
|
break;
|
|
342
345
|
case LX.TreeEvent.NODE_VISIBILITY:
|
|
343
346
|
if (e.node.trackData){
|
|
344
|
-
this.setTrackState( e.node.trackData.trackIdx, e.value );
|
|
347
|
+
this.setTrackState( e.node.trackData.trackIdx, e.value, false, false ); // do not update left panel
|
|
345
348
|
}
|
|
346
349
|
break;
|
|
347
350
|
}
|
|
@@ -358,6 +361,10 @@ class Timeline {
|
|
|
358
361
|
that.setTrackHeight( that.trackHeight );
|
|
359
362
|
}
|
|
360
363
|
|
|
364
|
+
if ( this.selectedTracks.length ){
|
|
365
|
+
this._updateTrackTreeSelection(); // select visible tracks
|
|
366
|
+
}
|
|
367
|
+
|
|
361
368
|
// setting a name in the addTree function adds an undesired node
|
|
362
369
|
this.trackTreesComponent.name = "tracksTrees";
|
|
363
370
|
p.components[this.trackTreesComponent.name] = this.trackTreesComponent;
|
|
@@ -406,7 +413,7 @@ class Timeline {
|
|
|
406
413
|
}
|
|
407
414
|
|
|
408
415
|
/**
|
|
409
|
-
* @param {
|
|
416
|
+
* @param {Object} options options for the new track
|
|
410
417
|
* { id: string, active: bool, locked: bool, }
|
|
411
418
|
* @returns
|
|
412
419
|
*/
|
|
@@ -425,8 +432,8 @@ class Timeline {
|
|
|
425
432
|
/**
|
|
426
433
|
* Finds tracks (wholy and partially) inside the range minY maxY.
|
|
427
434
|
* (Full) Canvas local coordinates.
|
|
428
|
-
* @param {
|
|
429
|
-
* @param {
|
|
435
|
+
* @param {Number} minY
|
|
436
|
+
* @param {Number} maxY
|
|
430
437
|
* @returns array of trackDatas
|
|
431
438
|
*/
|
|
432
439
|
getTracksInRange( minY, maxY ) {
|
|
@@ -466,14 +473,14 @@ class Timeline {
|
|
|
466
473
|
/**
|
|
467
474
|
* @method setAnimationClip
|
|
468
475
|
* @param {*} animation
|
|
469
|
-
* @param {
|
|
470
|
-
* @param {
|
|
476
|
+
* @param {Boolean} needsToProcess
|
|
477
|
+
* @param {Object} processOptions
|
|
471
478
|
* [KeyFrameTimeline] - each track should contain an attribute "dim" to indicate the value dimension (e.g. vector3 -> dim=3). Otherwise dimensions will be infered from track's values and times. Default is 1
|
|
472
479
|
*/
|
|
473
480
|
setAnimationClip( animation, needsToProcess = true ) {
|
|
474
481
|
|
|
475
482
|
this.deselectAllElements();
|
|
476
|
-
this.deselectAllTracks();
|
|
483
|
+
this.deselectAllTracks( false ); // no need to update left panel yet
|
|
477
484
|
|
|
478
485
|
this.selectedItems = [];
|
|
479
486
|
|
|
@@ -802,7 +809,7 @@ class Timeline {
|
|
|
802
809
|
* @method setScroll
|
|
803
810
|
* not delta from last state, but full scroll amount.
|
|
804
811
|
* @param {Number} scrollY either pixels or [0,1]
|
|
805
|
-
* @param {
|
|
812
|
+
* @param {Boolean} normalized if true, scrollY is in range[0,1] being 1 fully scrolled. Otherwised scrollY represents pixels
|
|
806
813
|
* @returns
|
|
807
814
|
*/
|
|
808
815
|
|
|
@@ -930,7 +937,6 @@ class Timeline {
|
|
|
930
937
|
this.movingKeys = false;
|
|
931
938
|
this.timeBeforeMove = null;
|
|
932
939
|
this.boxSelection = false; // after mouseup
|
|
933
|
-
this.deselectAllTracks();
|
|
934
940
|
}
|
|
935
941
|
|
|
936
942
|
|
|
@@ -1057,7 +1063,7 @@ class Timeline {
|
|
|
1057
1063
|
|
|
1058
1064
|
/**
|
|
1059
1065
|
* @method changeState
|
|
1060
|
-
* @param {
|
|
1066
|
+
* @param {Boolean} skipCallback defaults false
|
|
1061
1067
|
* @description change play/pause state
|
|
1062
1068
|
**/
|
|
1063
1069
|
changeState(skipCallback = false) {
|
|
@@ -1065,8 +1071,8 @@ class Timeline {
|
|
|
1065
1071
|
}
|
|
1066
1072
|
/**
|
|
1067
1073
|
* @method setState
|
|
1068
|
-
* @param {
|
|
1069
|
-
* @param {
|
|
1074
|
+
* @param {Boolean} state
|
|
1075
|
+
* @param {Boolean} skipCallback defaults false
|
|
1070
1076
|
* @description change play/pause state
|
|
1071
1077
|
**/
|
|
1072
1078
|
setState(state, skipCallback = false) {
|
|
@@ -1081,8 +1087,8 @@ class Timeline {
|
|
|
1081
1087
|
|
|
1082
1088
|
/**
|
|
1083
1089
|
* @method setLoopMode
|
|
1084
|
-
* @param {
|
|
1085
|
-
* @param {
|
|
1090
|
+
* @param {Boolean} loopState
|
|
1091
|
+
* @param {Boolean} skipCallback defaults false
|
|
1086
1092
|
* @description change loop mode of the timeline
|
|
1087
1093
|
*/
|
|
1088
1094
|
setLoopMode(loopState, skipCallback = false){
|
|
@@ -1116,11 +1122,14 @@ class Timeline {
|
|
|
1116
1122
|
}
|
|
1117
1123
|
|
|
1118
1124
|
/**
|
|
1119
|
-
* @param {
|
|
1120
|
-
* @param {
|
|
1125
|
+
* @param {Array} itemsToAdd [ trackIdx ], array of numbers identifying tracks by their index
|
|
1126
|
+
* @param {Array} itemsToRemove [ trackIdx ], array of numbers identifying tracks by their index
|
|
1121
1127
|
*/
|
|
1122
1128
|
changeSelectedItems( itemsToAdd = null, itemsToRemove = null, skipCallback = false ) {
|
|
1123
1129
|
|
|
1130
|
+
this.deselectAllElements();
|
|
1131
|
+
this.deselectAllTracks( false ); // no need to update left panel. It is going to be rebuilt anyways
|
|
1132
|
+
|
|
1124
1133
|
const tracks = this.animationClip.tracks;
|
|
1125
1134
|
|
|
1126
1135
|
if ( itemsToRemove ){
|
|
@@ -1144,7 +1153,6 @@ class Timeline {
|
|
|
1144
1153
|
}
|
|
1145
1154
|
}
|
|
1146
1155
|
|
|
1147
|
-
|
|
1148
1156
|
this.updateLeftPanel();
|
|
1149
1157
|
|
|
1150
1158
|
if(this.onItemSelected && !skipCallback){
|
|
@@ -1154,7 +1162,7 @@ class Timeline {
|
|
|
1154
1162
|
|
|
1155
1163
|
/**
|
|
1156
1164
|
* It will find the first occurrence of trackId in animationClip.tracks
|
|
1157
|
-
* @param {
|
|
1165
|
+
* @param {String} trackId
|
|
1158
1166
|
* @returns
|
|
1159
1167
|
*/
|
|
1160
1168
|
getTrack( trackId ){
|
|
@@ -1168,38 +1176,83 @@ class Timeline {
|
|
|
1168
1176
|
}
|
|
1169
1177
|
|
|
1170
1178
|
/**
|
|
1171
|
-
*
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
*/
|
|
1176
|
-
selectTrack( trackIdx ) {
|
|
1179
|
+
* @param {Boolean} updateTrackTree whether the track tree needs a refresh
|
|
1180
|
+
* @returns
|
|
1181
|
+
*/
|
|
1182
|
+
deselectAllTracks( updateTrackTree = true ) {
|
|
1177
1183
|
|
|
1178
1184
|
if( !this.animationClip ){
|
|
1179
1185
|
return;
|
|
1180
1186
|
}
|
|
1181
1187
|
|
|
1182
|
-
this.
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1188
|
+
const tracks = this.animationClip.tracks;
|
|
1189
|
+
for(let i = 0; i < tracks.length; i++){
|
|
1190
|
+
tracks[ i ].isSelected = false;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
this.selectedTracks.length = 0;
|
|
1194
|
+
|
|
1195
|
+
if ( updateTrackTree ){
|
|
1196
|
+
this._updateTrackTreeSelection();
|
|
1189
1197
|
}
|
|
1190
1198
|
}
|
|
1191
1199
|
|
|
1192
|
-
|
|
1193
|
-
|
|
1200
|
+
/**
|
|
1201
|
+
* @param {Int} trackIdx
|
|
1202
|
+
* @param {Boolean} isSelected new "selected" state of the track
|
|
1203
|
+
* @param {Boolean} skipCallback whether to call onSetTrackSelection
|
|
1204
|
+
* @param {Boolean} updateTrackTree whether track tree panel needs a refresh
|
|
1205
|
+
* @returns
|
|
1206
|
+
*/
|
|
1207
|
+
setTrackSelection( trackIdx, isSelected, skipCallback = false, updateTrackTree = true ){
|
|
1208
|
+
const track = this.animationClip.tracks[ trackIdx ];
|
|
1209
|
+
const oldValue = track.isSelected;
|
|
1210
|
+
track.isSelected = isSelected;
|
|
1211
|
+
|
|
1212
|
+
const idx = this.selectedTracks.indexOf( track );
|
|
1213
|
+
if ( ( idx == -1 && !isSelected ) || ( idx > -1 && isSelected ) ){
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
if ( idx == -1 ){
|
|
1218
|
+
this.selectedTracks.push( track );
|
|
1219
|
+
}else{
|
|
1220
|
+
this.selectedTracks.splice( idx, 1 );
|
|
1221
|
+
}
|
|
1194
1222
|
|
|
1195
|
-
if(
|
|
1196
|
-
|
|
1223
|
+
if( this.onSetTrackSelection && !skipCallback ){
|
|
1224
|
+
this.onSetTrackSelection(track, oldValue );
|
|
1197
1225
|
}
|
|
1198
1226
|
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1227
|
+
if ( updateTrackTree ){
|
|
1228
|
+
this._updateTrackTreeSelection();
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
/**
|
|
1233
|
+
* updates trackTreesComponent's nodes, to match the selectedTracks
|
|
1234
|
+
*/
|
|
1235
|
+
_updateTrackTreeSelection(){
|
|
1236
|
+
const data = this.trackTreesComponent.innerTree.data;
|
|
1237
|
+
const selected = this.trackTreesComponent.innerTree.selected;
|
|
1238
|
+
selected.length = 0;
|
|
1239
|
+
|
|
1240
|
+
const addToSelection = (nodes) =>{
|
|
1241
|
+
for( let i = 0; i < nodes.length; ++i ){
|
|
1242
|
+
if ( nodes[i].trackData && nodes[i].trackData.isSelected ){
|
|
1243
|
+
selected.push( nodes[i] );
|
|
1244
|
+
}
|
|
1245
|
+
if ( nodes[i].children ){
|
|
1246
|
+
addToSelection( nodes[i].children );
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1202
1249
|
}
|
|
1250
|
+
|
|
1251
|
+
// update innerTree (visible) selected nodes
|
|
1252
|
+
if ( this.selectedTracks.length ){
|
|
1253
|
+
addToSelection( data );
|
|
1254
|
+
}
|
|
1255
|
+
this.trackTreesComponent.innerTree.refresh();
|
|
1203
1256
|
}
|
|
1204
1257
|
|
|
1205
1258
|
deselectAllElements(){
|
|
@@ -1208,22 +1261,53 @@ class Timeline {
|
|
|
1208
1261
|
|
|
1209
1262
|
/**
|
|
1210
1263
|
* @method setTrackState
|
|
1211
|
-
* @param {
|
|
1212
|
-
* @param {
|
|
1264
|
+
* @param {Int} trackIdx
|
|
1265
|
+
* @param {Boolean} isEnbaled
|
|
1266
|
+
* @param {Boolean} skipCallback onSetTrackState
|
|
1267
|
+
* @param {Boolean} updateTrackTree updates eye icon of the track, if it is visible in the timeline
|
|
1213
1268
|
*/
|
|
1214
|
-
setTrackState(trackIdx, isEnbaled = true, skipCallback = false) {
|
|
1269
|
+
setTrackState(trackIdx, isEnbaled = true, skipCallback = false, updateTrackTree = true ) {
|
|
1215
1270
|
const track = this.animationClip.tracks[trackIdx];
|
|
1216
1271
|
|
|
1217
1272
|
const oldState = track.active;
|
|
1218
1273
|
track.active = isEnbaled;
|
|
1219
1274
|
|
|
1220
|
-
if(this.onSetTrackState && !skipCallback)
|
|
1275
|
+
if ( this.onSetTrackState && !skipCallback ){
|
|
1221
1276
|
this.onSetTrackState(track, oldState);
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
if ( updateTrackTree && !this.skipVisibility ){
|
|
1280
|
+
// TODO: a bit of an overkill. Maybe searching the node in the tree is less expensive
|
|
1281
|
+
this.updateLeftPanel();
|
|
1282
|
+
}
|
|
1222
1283
|
}
|
|
1223
1284
|
|
|
1224
1285
|
/**
|
|
1225
|
-
*
|
|
1226
|
-
* @param {
|
|
1286
|
+
*
|
|
1287
|
+
* @param {Int} trackIdx
|
|
1288
|
+
* @param {Boolean} isLocked
|
|
1289
|
+
* @param {Boolean} skipCallback onSetTrackLock
|
|
1290
|
+
* @param {Boolean} updateTrackTree updates lock icon of the track, if it is visible in the timeline
|
|
1291
|
+
*/
|
|
1292
|
+
setTrackLock(trackIdx, isLocked = false, skipCallback = false, updateTrackTree = true ){
|
|
1293
|
+
const track = this.animationClip.tracks[trackIdx];
|
|
1294
|
+
|
|
1295
|
+
const oldState = track.locked;
|
|
1296
|
+
track.locked = isLocked;
|
|
1297
|
+
|
|
1298
|
+
if ( this.onSetTrackLock && !skipCallback ){
|
|
1299
|
+
this.onSetTrackLock( track, oldState );
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
if ( updateTrackTree && !this.skipLock ){
|
|
1303
|
+
// TODO: a bit of an overkill. Maybe searching the node in the tree is less expensive
|
|
1304
|
+
this.updateLeftPanel();
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
/**
|
|
1309
|
+
* @param {Int} trackIdx index of track in the animation (not local index)
|
|
1310
|
+
* @param {Boolean} combineWithPrevious whether to create a new entry or unify changes into a single undo entry
|
|
1227
1311
|
*/
|
|
1228
1312
|
saveState( trackIdx, combineWithPrevious = false ) {
|
|
1229
1313
|
if ( !this.historySaveEnabler ){ return; }
|
|
@@ -1339,10 +1423,7 @@ class Timeline {
|
|
|
1339
1423
|
'icon': (track.locked ? 'TimelineLock' : 'TimelineLockOpen'),
|
|
1340
1424
|
'swap': (track.locked ? 'TimelineLockOpen' : 'TimelineLock'),
|
|
1341
1425
|
'callback': (node, swapValue, event) => {
|
|
1342
|
-
node.trackData.
|
|
1343
|
-
if(this.onLockTrack){
|
|
1344
|
-
this.onLockTrack(node.trackData, node);
|
|
1345
|
-
}
|
|
1426
|
+
this.setTrackLock( node.trackData.trackIdx, !node.trackData.locked, false, false ); // do not update left panel
|
|
1346
1427
|
}
|
|
1347
1428
|
}]});
|
|
1348
1429
|
}
|
|
@@ -1352,7 +1433,7 @@ class Timeline {
|
|
|
1352
1433
|
|
|
1353
1434
|
/**
|
|
1354
1435
|
*
|
|
1355
|
-
* @param {
|
|
1436
|
+
* @param {Object} options set some values for the track instance (groups and trackIdx not included)
|
|
1356
1437
|
* @returns
|
|
1357
1438
|
*/
|
|
1358
1439
|
instantiateTrack(options = {}, clone = false) {
|
|
@@ -1361,7 +1442,7 @@ class Timeline {
|
|
|
1361
1442
|
id: options.id ?? ( Math.floor(performance.now().toString()) + "_" + Math.floor(Math.random() * 0xffff) ), //must be unique, at least inside a group
|
|
1362
1443
|
active: options.active ?? true,
|
|
1363
1444
|
locked: options.locked ?? false,
|
|
1364
|
-
isSelected: false, // render
|
|
1445
|
+
isSelected: false, // render and lexgui tree
|
|
1365
1446
|
trackIdx: -1,
|
|
1366
1447
|
data: options.data ?? null // user defined data
|
|
1367
1448
|
}
|
|
@@ -1369,7 +1450,7 @@ class Timeline {
|
|
|
1369
1450
|
|
|
1370
1451
|
/**
|
|
1371
1452
|
* Generates an animationClip using either the parameters set in the animation argument or using default values
|
|
1372
|
-
* @param {
|
|
1453
|
+
* @param {Object} animation data with which to generate an animationClip
|
|
1373
1454
|
* @returns
|
|
1374
1455
|
*/
|
|
1375
1456
|
instantiateAnimationClip(options, clone = false) {
|
|
@@ -1421,8 +1502,8 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1421
1502
|
|
|
1422
1503
|
static ADDKEY_VALUESINARRAYS = 0x01; // addkeyframes as [ [k0v0, k0v1...], [k1v0, k1v1...] ] instead of [k0v0,k0v1,k1v0,k1v1]
|
|
1423
1504
|
/**
|
|
1424
|
-
* @param {
|
|
1425
|
-
* @param {
|
|
1505
|
+
* @param {String} name unique string
|
|
1506
|
+
* @param {Object} options = {animationClip, selectedItems, x, y, width, height, canvas, trackHeight}
|
|
1426
1507
|
*/
|
|
1427
1508
|
constructor(name, options = {}) {
|
|
1428
1509
|
|
|
@@ -1460,10 +1541,7 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1460
1541
|
'icon': (track.locked ? 'TimelineLock' : 'TimelineLockOpen'),
|
|
1461
1542
|
'swap': (track.locked ? 'TimelineLockOpen' : 'TimelineLock'),
|
|
1462
1543
|
'callback': (node, swapValue, event) => {
|
|
1463
|
-
node.trackData.
|
|
1464
|
-
if(this.onLockTrack){
|
|
1465
|
-
this.onLockTrack(node.trackData, node);
|
|
1466
|
-
}
|
|
1544
|
+
this.setTrackLock( node.trackData.trackIdx, !node.trackData.locked, false, false ); // do not update left panel
|
|
1467
1545
|
}
|
|
1468
1546
|
}]});
|
|
1469
1547
|
}
|
|
@@ -1483,7 +1561,7 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1483
1561
|
|
|
1484
1562
|
/**
|
|
1485
1563
|
* OVERRIDE
|
|
1486
|
-
* @param {
|
|
1564
|
+
* @param {Object} options track information that wants to be set to the new track
|
|
1487
1565
|
* id, dim, values, times, selected, edited, hovered
|
|
1488
1566
|
* @returns
|
|
1489
1567
|
*/
|
|
@@ -1528,7 +1606,7 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1528
1606
|
|
|
1529
1607
|
/**
|
|
1530
1608
|
* Generates an animationClip using either the parameters set in the animation argument or using default values
|
|
1531
|
-
* @param {
|
|
1609
|
+
* @param {Object} animation data with which to generate an animationClip
|
|
1532
1610
|
* @returns
|
|
1533
1611
|
*/
|
|
1534
1612
|
instantiateAnimationClip(animation, clone = false) {
|
|
@@ -1617,12 +1695,13 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1617
1695
|
|
|
1618
1696
|
/**
|
|
1619
1697
|
* OVERRIDE
|
|
1620
|
-
* @param {
|
|
1621
|
-
* @param {
|
|
1698
|
+
* @param {Array} itemsToAdd [ trackIdx, "groupId" ], array of strings and/or number identifying groups and/or tracks
|
|
1699
|
+
* @param {Array} itemsToRemove [ trackIdx, "groupId" ], array of strings and/or number identifying groups and/or tracks
|
|
1622
1700
|
*/
|
|
1623
1701
|
changeSelectedItems( itemsToAdd = null, itemsToRemove = null, skipCallback = false ) {
|
|
1624
1702
|
|
|
1625
1703
|
this.deselectAllElements();
|
|
1704
|
+
this.deselectAllTracks( false ); // no need to update left panel. It is going to be rebuilt anyways
|
|
1626
1705
|
|
|
1627
1706
|
const tracks = this.animationClip.tracks;
|
|
1628
1707
|
const tracksPerGroup = this.animationClip.tracksPerGroup;
|
|
@@ -1662,8 +1741,8 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1662
1741
|
}
|
|
1663
1742
|
|
|
1664
1743
|
/**
|
|
1665
|
-
* @param {
|
|
1666
|
-
* @param {
|
|
1744
|
+
* @param {String} groupId unique identifier
|
|
1745
|
+
* @param {Array} groupTracks [ "trackID", trackIdx ] array of strings and/or numbers of the existing tracks to include in this group. A track can only be part of 1 group
|
|
1667
1746
|
* if groupTracks == null, groupId is removed from the list
|
|
1668
1747
|
*/
|
|
1669
1748
|
setTracksGroup( groupId, groupTracks = null ){
|
|
@@ -1731,7 +1810,7 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1731
1810
|
}
|
|
1732
1811
|
|
|
1733
1812
|
/**
|
|
1734
|
-
* @param {
|
|
1813
|
+
* @param {String} groupId
|
|
1735
1814
|
* @returns array of tracks or null
|
|
1736
1815
|
*/
|
|
1737
1816
|
getTracksGroup( groupId ){
|
|
@@ -1740,8 +1819,8 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1740
1819
|
|
|
1741
1820
|
/**
|
|
1742
1821
|
* OVERRIDE
|
|
1743
|
-
* @param {
|
|
1744
|
-
* @param {
|
|
1822
|
+
* @param {String} trackId
|
|
1823
|
+
* @param {String} groupId optionl. If not set, it will find the first occurrence of trackId in animationClip.tracks
|
|
1745
1824
|
* @returns
|
|
1746
1825
|
*/
|
|
1747
1826
|
getTrack( trackId, groupId = null ){
|
|
@@ -1759,8 +1838,8 @@ class KeyFramesTimeline extends Timeline {
|
|
|
1759
1838
|
|
|
1760
1839
|
/**
|
|
1761
1840
|
*
|
|
1762
|
-
* @param {
|
|
1763
|
-
* @param {
|
|
1841
|
+
* @param {Number} size pixels, height of keyframe
|
|
1842
|
+
* @param {Number} sizeHovered optional, size in pixels when hovered
|
|
1764
1843
|
*/
|
|
1765
1844
|
setKeyframeSize( size, sizeHovered = null ){
|
|
1766
1845
|
this.keyframeSizeHovered = sizeHovered ?? size;
|
|
@@ -2300,7 +2379,7 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2300
2379
|
|
|
2301
2380
|
/**
|
|
2302
2381
|
* updates an existing track with new values and times.
|
|
2303
|
-
* @param {
|
|
2382
|
+
* @param {Int} trackIdx index of track in the animationClip
|
|
2304
2383
|
* @param {*} newTrack object with two arrays: values and times. These will be set to the selected track
|
|
2305
2384
|
* @returns
|
|
2306
2385
|
*/
|
|
@@ -2322,8 +2401,8 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2322
2401
|
* removes equivalent sequential keys either because of equal times or values
|
|
2323
2402
|
* (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0)
|
|
2324
2403
|
* @param {Int} trackIdx index of track in the animation
|
|
2325
|
-
* @param {
|
|
2326
|
-
* @param {
|
|
2404
|
+
* @param {Boolean} onlyEqualTime if true, removes only keyframes with equal times. Otherwise, values are ALSO compared through the class threshold
|
|
2405
|
+
* @param {Boolean} skipCallback if false, triggers "onOptimizeTracks" after optimizing
|
|
2327
2406
|
*/
|
|
2328
2407
|
optimizeTrack(trackIdx, onlyEqualTime = false, skipCallback = false ) {
|
|
2329
2408
|
if ( !this.animationClip ){ return; }
|
|
@@ -2333,6 +2412,11 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2333
2412
|
values = track.values,
|
|
2334
2413
|
stride = track.dim,
|
|
2335
2414
|
threshold = this.optimizeThreshold;
|
|
2415
|
+
|
|
2416
|
+
if ( track.locked ){
|
|
2417
|
+
return;
|
|
2418
|
+
}
|
|
2419
|
+
|
|
2336
2420
|
let cmpFunction = (v, p, n, t) => { return Math.abs(v - p) >= t || Math.abs(v - n) >= t };
|
|
2337
2421
|
let lastSavedIndex = 0;
|
|
2338
2422
|
const lastIndex = times.length-1;
|
|
@@ -2675,7 +2759,7 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2675
2759
|
|
|
2676
2760
|
pasteKeyFrameValue( track, index ) {
|
|
2677
2761
|
|
|
2678
|
-
if(this.clipboard.value.type != track.type){
|
|
2762
|
+
if(track.locked || this.clipboard.value.type != track.type){
|
|
2679
2763
|
return;
|
|
2680
2764
|
}
|
|
2681
2765
|
|
|
@@ -2720,6 +2804,10 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2720
2804
|
const values = clipboardInfo.values;
|
|
2721
2805
|
const track = this.animationClip.tracks[trackIdx];
|
|
2722
2806
|
|
|
2807
|
+
if( track.locked ){
|
|
2808
|
+
continue;
|
|
2809
|
+
}
|
|
2810
|
+
|
|
2723
2811
|
this.saveState(track.trackIdx, trackCount++);
|
|
2724
2812
|
this.historySaveEnabler = false;
|
|
2725
2813
|
this.addKeyFrames( track.trackIdx, values, times, -globalStart + pasteTime, KeyFramesTimeline.ADDKEY_VALUESINARRAYS );
|
|
@@ -2737,11 +2825,11 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2737
2825
|
|
|
2738
2826
|
/**
|
|
2739
2827
|
*
|
|
2740
|
-
* @param {
|
|
2741
|
-
* @param {
|
|
2742
|
-
* @param {
|
|
2743
|
-
* @param {
|
|
2744
|
-
* @param {
|
|
2828
|
+
* @param {Int} trackIdx
|
|
2829
|
+
* @param {Array} newValues array of values for each keyframe. It should be a flat array of size track.dim*numKeyframes. Check ADDKEY_VALUESINARRAYS flag
|
|
2830
|
+
* @param {Array of numbers} newTimes must be ordered ascendently
|
|
2831
|
+
* @param {Number} timeOffset
|
|
2832
|
+
* @param {Int} flags
|
|
2745
2833
|
* KeyFramesTimeline.ADDKEY_VALUESINARRAYS: if set, newValues is an array of arrays, one for each entry [ [1,2,3], [5,6,7] ]. Times is still a flat array of values [ 0, 0.2 ]
|
|
2746
2834
|
|
|
2747
2835
|
* @returns
|
|
@@ -2749,7 +2837,7 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2749
2837
|
addKeyFrames( trackIdx, newValues, newTimes, timeOffset = 0, flags = 0x00 ){
|
|
2750
2838
|
const track = this.animationClip.tracks[trackIdx];
|
|
2751
2839
|
|
|
2752
|
-
if ( !newTimes.length ){ return; }
|
|
2840
|
+
if ( !newTimes.length || track.locked ){ return null; }
|
|
2753
2841
|
|
|
2754
2842
|
const valueDim = track.dim;
|
|
2755
2843
|
const trackTimes = track.times;
|
|
@@ -2839,6 +2927,7 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2839
2927
|
return;
|
|
2840
2928
|
}
|
|
2841
2929
|
|
|
2930
|
+
const tracks = this.animationClip.tracks;
|
|
2842
2931
|
const firstTrack = this.lastKeyFramesSelected[0][0];
|
|
2843
2932
|
let trackToRemove = firstTrack;
|
|
2844
2933
|
let toDelete = []; // indices to delete of the same track
|
|
@@ -2848,6 +2937,12 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2848
2937
|
const numSelected = this.lastKeyFramesSelected.length;
|
|
2849
2938
|
for( let i = 0; i < numSelected; ++i ){
|
|
2850
2939
|
const [trackIdx, frameIdx] = this.lastKeyFramesSelected[i];
|
|
2940
|
+
|
|
2941
|
+
if ( tracks[trackIdx].locked ){
|
|
2942
|
+
tracks[trackIdx].selected[frameIdx] = false; // unselect
|
|
2943
|
+
continue;
|
|
2944
|
+
}
|
|
2945
|
+
|
|
2851
2946
|
if ( trackToRemove != trackIdx ){
|
|
2852
2947
|
this.saveState(trackToRemove, trackToRemove != firstTrack);
|
|
2853
2948
|
|
|
@@ -2859,7 +2954,7 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2859
2954
|
toDelete.length = 0;
|
|
2860
2955
|
}
|
|
2861
2956
|
|
|
2862
|
-
toDelete.push(frameIdx)
|
|
2957
|
+
toDelete.push( frameIdx );
|
|
2863
2958
|
}
|
|
2864
2959
|
|
|
2865
2960
|
this.saveState(trackToRemove, trackToRemove != firstTrack);
|
|
@@ -2874,7 +2969,7 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2874
2969
|
deleteKeyFrames( trackIdx, indices, skipCallback = false ){
|
|
2875
2970
|
const track = this.animationClip.tracks[trackIdx];
|
|
2876
2971
|
|
|
2877
|
-
if ( !indices.length ){
|
|
2972
|
+
if ( !indices.length || track.locked ){
|
|
2878
2973
|
return false;
|
|
2879
2974
|
}
|
|
2880
2975
|
|
|
@@ -2923,9 +3018,9 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2923
3018
|
|
|
2924
3019
|
/**
|
|
2925
3020
|
* Binary search. Relies on track.times being a sorted array
|
|
2926
|
-
* @param {
|
|
2927
|
-
* @param {
|
|
2928
|
-
* @param {
|
|
3021
|
+
* @param {Object} track
|
|
3022
|
+
* @param {Number} time
|
|
3023
|
+
* @param {Number} mode on of the possible values
|
|
2929
3024
|
* - -1 = nearest frame with t[f] <= time
|
|
2930
3025
|
* - 0 = nearest frame
|
|
2931
3026
|
* - 1 = nearest frame with t[f] >= time
|
|
@@ -2967,9 +3062,9 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2967
3062
|
|
|
2968
3063
|
/**
|
|
2969
3064
|
* get the nearest keyframe to "time" given a maximum threshold.
|
|
2970
|
-
* @param {
|
|
2971
|
-
* @param {
|
|
2972
|
-
* @param {
|
|
3065
|
+
* @param {Object} track
|
|
3066
|
+
* @param {Number} time
|
|
3067
|
+
* @param {Number} threshold must be positive value
|
|
2973
3068
|
* @returns returns a postive/zero value if there is a frame inside the threshold range. Otherwise, -1
|
|
2974
3069
|
*/
|
|
2975
3070
|
getCurrentKeyFrame( track, time, threshold = 0.0 ) {
|
|
@@ -2987,10 +3082,10 @@ class KeyFramesTimeline extends Timeline {
|
|
|
2987
3082
|
|
|
2988
3083
|
/**
|
|
2989
3084
|
* Returns the interval of frames between minTime and maxTime (both included)
|
|
2990
|
-
* @param {
|
|
2991
|
-
* @param {
|
|
2992
|
-
* @param {
|
|
2993
|
-
* @param {
|
|
3085
|
+
* @param {Object} track
|
|
3086
|
+
* @param {Number} minTime
|
|
3087
|
+
* @param {Number} maxTime
|
|
3088
|
+
* @param {Number} threshold must be positive value
|
|
2994
3089
|
* @returns an array with two values [ minFrame, maxFrame ]. Otherwise null
|
|
2995
3090
|
*/
|
|
2996
3091
|
getKeyFramesInRange( track, minTime, maxTime, threshold = 0.0 ) {
|
|
@@ -3123,12 +3218,12 @@ class KeyFramesTimeline extends Timeline {
|
|
|
3123
3218
|
|
|
3124
3219
|
const track = this.animationClip.tracks[trackIdx];
|
|
3125
3220
|
|
|
3126
|
-
if(track.locked ){
|
|
3127
|
-
return;
|
|
3128
|
-
}
|
|
3129
|
-
|
|
3130
3221
|
this.unHoverAll();
|
|
3131
3222
|
this.deselectAllKeyFrames();
|
|
3223
|
+
|
|
3224
|
+
if( track.locked ){
|
|
3225
|
+
return;
|
|
3226
|
+
}
|
|
3132
3227
|
|
|
3133
3228
|
this.saveState(track.trackIdx);
|
|
3134
3229
|
|
|
@@ -3154,8 +3249,8 @@ class ClipsTimeline extends Timeline {
|
|
|
3154
3249
|
static CLONEREASON_TRACKCLONE = 4;
|
|
3155
3250
|
|
|
3156
3251
|
/**
|
|
3157
|
-
* @param {
|
|
3158
|
-
* @param {
|
|
3252
|
+
* @param {String} name
|
|
3253
|
+
* @param {Object} options = {animationClip, selectedItems, x, y, width, height, canvas, trackHeight}
|
|
3159
3254
|
*/
|
|
3160
3255
|
constructor(name, options = {}) {
|
|
3161
3256
|
|
|
@@ -3170,7 +3265,7 @@ class ClipsTimeline extends Timeline {
|
|
|
3170
3265
|
|
|
3171
3266
|
/**
|
|
3172
3267
|
* Generates an animationClip using either the parameters set in the animation argument or using default values
|
|
3173
|
-
* @param {
|
|
3268
|
+
* @param {Object} animation data with which to generate an animationClip
|
|
3174
3269
|
* @returns
|
|
3175
3270
|
*/
|
|
3176
3271
|
instantiateAnimationClip(animation, clone = false) {
|
|
@@ -3192,7 +3287,7 @@ class ClipsTimeline extends Timeline {
|
|
|
3192
3287
|
|
|
3193
3288
|
/**
|
|
3194
3289
|
*
|
|
3195
|
-
* @param {
|
|
3290
|
+
* @param {Object} options set some values for the track instance (groups and trackIdx not included)
|
|
3196
3291
|
* @returns
|
|
3197
3292
|
*/
|
|
3198
3293
|
instantiateTrack(options = {}, clone = false) {
|
|
@@ -3293,6 +3388,7 @@ class ClipsTimeline extends Timeline {
|
|
|
3293
3388
|
changeSelectedItems( ) {
|
|
3294
3389
|
|
|
3295
3390
|
this.deselectAllElements();
|
|
3391
|
+
this.deselectAllTracks( false ); // no need to update left
|
|
3296
3392
|
|
|
3297
3393
|
this.selectedItems = this.animationClip.tracks.slice();
|
|
3298
3394
|
|
|
@@ -3924,10 +4020,10 @@ class ClipsTimeline extends Timeline {
|
|
|
3924
4020
|
|
|
3925
4021
|
/**
|
|
3926
4022
|
*
|
|
3927
|
-
* @param {
|
|
3928
|
-
* @param {
|
|
3929
|
-
* @param {
|
|
3930
|
-
* @param {
|
|
4023
|
+
* @param {Object} clip clip to be added
|
|
4024
|
+
* @param {Int} trackIdx (optional) track where to put the clip. -1 will find the first free slot. ***WARNING*** Must call getClipsInRange, before calling this function with a valid trackdIdx
|
|
4025
|
+
* @param {Number} offsetTime (optional) offset time of current time
|
|
4026
|
+
* @param {Number} searchStartTrackIdx (optional) if trackIdx is set to -1, this idx will be used as the starting point to find a valid track
|
|
3931
4027
|
* @returns a zero/positive value if successful. Otherwise, -1
|
|
3932
4028
|
*/
|
|
3933
4029
|
addClip( clip, trackIdx = -1, offsetTime = 0, searchStartTrackIdx = 0 ) {
|
|
@@ -4174,8 +4270,8 @@ class ClipsTimeline extends Timeline {
|
|
|
4174
4270
|
/**
|
|
4175
4271
|
* User defined. Used when copying and pasting
|
|
4176
4272
|
* @param {Array of clips} clipsToClone array of original clips. Do not modify clips in this array
|
|
4177
|
-
* @param {
|
|
4178
|
-
* @param {
|
|
4273
|
+
* @param {Number} timeOffset Value of time that should be added (or subtracted) from the timing attributes
|
|
4274
|
+
* @param {Int} reason Flag to signal the reason of the clone
|
|
4179
4275
|
* @returns {Array of clips}
|
|
4180
4276
|
*/
|
|
4181
4277
|
cloneClips( clipsToClone, timeOffset, reason = 0 ){
|