/* SEQUENCE GROUPING MANAGEMENT MODULE this module builds a representation of a sequence in values using containers It allows to create groups of keys, a group being a cue and one key per layer. In a group, all keys sync their time on cue's time, only value can be modified The sequence with grouping can then be exported and reloaded. The module checks periodicaly if timeline sequence structure (cues, layers and keys) and module values structure are in sync. If out of sync, you may either rebuild values structure from sequence or rebuild sequence from values */ var syncRatems = 100; var syncDelta = 0.; var doSync = false ; var inibValuesChanged = false ; var debug = true; var debugFile; var parametersPath = local.parameters ; var sequence ; var sequencePath = local.parameters.sequence ; var file ; var filePath = local.parameters.sequenceFile ; var newGroupLayers = local.values.newGroup.layers ; var groups = local.getChild("values").groups ; var cues = local.getChild("values").keys.cues ; // var cuesList = []; //holds cues containers reference var layers = local.getChild("values").keys.layers ; // var layerList = {}; //holds layer container as object with reference and an array of keys containers references var rebuildValues = script.addTrigger("rebuild values", "adds a key at current cursor position" ); var refreshKeys = script.addTrigger("refresh keys", "truc"); var printKeys = script.addTrigger("printKeys","truc"); var clearSeq = script.addTrigger("clear Sequence","truc"); var sequence_structure = { params: {}, groups:{}, keys:{ cues:{}, layers:{} } }; //WAS tl_groups /*group0 : { inCue : {pan : "key", tilt : "key", zoom : "key", dim : "key", focus : "key"}, outCue : {pan : "key", tilt : "key", zoom : "key", dim : "key", focus : "key"} }*/ ///////////////////// SCRIPT FUNCTIONS ////////////////////////////// function init() { script.log("LOADED SEQUENCE GROUPING MANAGEMENT MODULE "); //adapt update rate updateSyncRate(); updateSynchronize(); // layers = local.getChild("values").keys.layers ; // printMethodsAndProperties(layers); //test if a sequence is already choosen else refresh and unlock sequence enum sequence = sequencePath.getKey(); if(sequence == ""){refreshSequenceEnum();} else{sequencePath.setAttribute("readonly", true);} } function scriptParameterChanged(param){ if (param.is(printKeys) ){printKeys();} else if(param.is(rebuildValues)){struct_buildValuesFromSeq();} else if (param.is(refreshKeys)){ script.log("refreshKeys"); refreshKeysList(); } else if (param.is(clearSeq)){seq_clearSequence();} else {script.log("scriptparamchanged" + param.name);} } function update(deltaTime){ if(doSync){ syncDelta += deltaTime ; // script.log(syncDelta); if (syncDelta >= syncRatems){ syncDelta = 0. ; struct_buildValuesFromSeq(); } } } /////////////////////////// HELPERS ///////////////////////////////// function printMethodsAndProperties(obj){ script.log(" "); var name = obj.getControlAddress() == undefined ? "object" : obj.getControlAddress(); // var name = "object"; script.log(" METHODS OF " + name); var methods = util.getObjectMethods(obj); for(var i=0 ; i<methods.length ; i++){ script.log(" "+ methods[i]); } script.log(" PARAMETERS OF " + name); var props = util.getObjectProperties(obj, true, false); for(var i=0 ; i<props.length ; i++){ script.log(" "+ props[i]); } script.log(" OBJECTS OF " + name); var props = util.getObjectProperties(obj, false, true); for(var i=0 ; i<props.length ; i++){ script.log(" "+ props[i]); } } function printKeys(){ var seq = root.sequences.sequence.layers.mapping.automation.getItems() ; if(seq.length){ for(var i=0; i<seq.length; i++){ script.log (">> "+seq[i].name + " " + seq[i].position.get() + " " + seq[i].listSize.get() ); } } else{script.log("nothing to print");} } /////////////////////// PARAMETERS FUNCTIONS /////////////////////// // Module callback function moduleParameterChanged(param){ if (param.is(parametersPath.importExport.sequenceFile)) {} else if (param.is(parametersPath.importExport.loadSequence)){importSequenceFile();} else if (param.is(parametersPath.importExport.exportSequence)) { exportSequenceFile();} else if (param.is(parametersPath.synchronize)){updateSynchronize();} else if (param.is(parametersPath.syncRate)){updateSyncRate();} else if (param.is(parametersPath.changeSequence)){refreshSequenceEnum();} else if (param.is(parametersPath.sequence)){ sequence = sequencePath.getKey(); script.log("watching "+ sequence); } else{script.log("received module param :" + param.name);} } function updateSynchronize(){ doSync = parametersPath.synchronize.get(); script.log(doSync ? "sync ON" : "sync OFF" ); } function updateSyncRate(){ syncRatems = parametersPath.syncRate.get() / 1000.; script.log("new sync rate : " + syncRatems); // script.setUpdateRate(syncRatems); } //refresh Enum function refreshSequenceEnum(){ script.log("refresh list of sequences"); local.parameters.sequence.setAttribute("readonly",false); if(root.sequences.getItems().length){ local.parameters.sequence.removeOptions(); for(var i=0 ; i<root.sequences.getItems().length ; i++ ){ local.parameters.sequence.addOption(root.sequences.getItems()[i].name,i); } } } function testSequenceFile(){ var file = local.parameters.sequenceFile; //test if a file is declared if( file.getAbsolutePath() != ""){ script.log("testing file " + file.getAbsolutePath() ); var data = file.readFile(true); // test if expected fields are absent if (data.groups == undefined){ script.log("file structure not recognized"); refreshSequenceEnum(); } //if all is well, let's load in sequence_structure else { script.log("ready to import"); return 1; // loadSequenceFile(); } } else { script.log("no file to load"); refreshSequenceEnum(); return 0; } } function importSequenceFile(){ script.log("loading file"); sequence_structure = local.parameters.sequenceFile.readFile(true); //lock enum with curent sequence name var sequenceList = local.parameters.sequence; sequenceList.removeOptions(); sequenceList.addOption(sequence_structure.params.sequence, 0); local.parameters.sequence.setAttribute("readonly",true); } function exportSequenceFile(){ script.log("exporting file"); local.parameters.sequenceFile.writeFile(JSON.stringify(sequence_structure), true); } /////////////////////// VALUES FUNCTIONS /////////////////////// /* Values hold all keys, presented in group containers a nogroup container shows keys that are not grouped */ // Value callback function moduleValueChanged(value){ if(!inibValuesChanged){ doSync = false; script.log(value.getParent().name); doSync = true; } if (value.is(local.values.newGroup.create)){createNewGroup();} // script.log(value.getParent().name); // if(value.getParent().name = "cue"){script.log("changed");} if (value.name == "addGroup"){createNewGroup();} } //rebuild values.keys structure from sequence function struct_buildValuesFromSeq(){ inibValuesChanged = true; var tl_seq = root.sequences.getItemWithName(sequence); //build cues if needed var tl_cues = tl_seq.cues.getItems(); cues = local.getChild("values").keys.cues ; var cuesList = util.getObjectProperties(cues, true, false); // script.log("cueList : " +cuesList.length); // script.log("tl List : " +tl_cues.length); if (cuesList.length != tl_cues.length){ var delta = cuesList.length-tl_cues.length; var addRemove = delta > 0 ? false : true ; //0 to remove, 1 to add delta = Math.abs(delta); script.log("change "+ delta + (addRemove ? " up" : " down")); for(var i=0; i<delta; i++){ // script.log(i); if(addRemove){ var newCue = cues.addContainer("newCue"+i); newCue.addFloatParameter("time", "position in time", 0., 0.); newCue.setCollapsed(true); } else { script.log(util.getObjectProperties(cues, true, false)[0]); cues.removeContainer(util.getObjectProperties(cues, true, false)[0]); } // cuesList = ; } } //update cues cues = local.getChild("values").keys.cues ; var cuesList = util.getObjectProperties(cues, true, false); if(tl_cues.length){ for (var i=0; i<tl_cues.length; i++){ var currentCue = cues.getChild(cuesList[i]); currentCue.setName(tl_cues[i].name); currentCue.time.set(tl_cues[i].time.get()); } } //build layers if needed var layersChanged = false ; var tl_layers = tl_seq.layers.getItems(); layers = local.getChild("values").keys.layers ; var layersList = util.getObjectProperties(layers, true, false); if (layersList.length != tl_layers.length){ layersChanged = true ; var delta = layersList.length-tl_layers.length; var addRemove = delta > 0 ? false : true ; //0 to remove, 1 to add delta = Math.abs(delta); script.log("change "+ delta + (addRemove ? " up" : " down")); for(var i=0; i<delta; i++){ // script.log(i); if(addRemove){ var newLayer = layers.addContainer("newLayer"+i); // newLayer.addFloatParameter("time", "position in time", 0., 0.); newLayer.setCollapsed(true); } else { script.log(util.getObjectProperties(layers, true, false)[0]); layers.removeContainer(util.getObjectProperties(layers, true, false)[0]); } // cuesList = ; } } //update layers value if there was a change in structure if(layersChanged){ layers = local.getChild("values").keys.layers ; var layerList = util.getObjectProperties(layers, true, false); if(tl_layers.length){ for (var i=0; i<tl_layers.length; i++){ var currentLayer = layers.getChild(layerList[i]); currentLayer.setName(tl_layers[i].name); // } } } //build keys if needed layers = local.getChild("values").keys.layers ; layersList = util.getObjectProperties(layers, true, false); if(layersList.length){ for(var layerIndex=0; layerIndex<layersList.length; layerIndex++){ var currentLayer = layers.getChild(layersList[layerIndex]); var keyList = util.getObjectProperties(currentLayer, true, false); var tl_lay = tl_seq.layers.getItemWithName(layersList[layerIndex]); var tl_keys; if(tl_lay.automation!=undefined){tl_keys = tl_lay.automation.getItems();} //rebuild key structure for currentLayer if (keyList.length != tl_keys.length){ var delta = keyList.length-tl_keys.length; var addRemove = delta > 0 ? false : true ; //0 to remove, 1 to add delta = Math.abs(delta); script.log("change "+ delta + (addRemove ? " up" : " down")); for(var i=0; i<delta; i++){ // script.log(i); if(addRemove){ var newKey = currentLayer.addContainer("newkey"+i); newKey.addFloatParameter("time", "position in time", 0., 0.); newKey.addFloatParameter("value", "vertical value", 0., 0.); newKey.setCollapsed(true); } else { script.log(util.getObjectProperties(currentLayer, true, false)[0]); currentLayer.removeContainer(util.getObjectProperties(currentLayer, true, false)[0]); } } } currentLayer = layers.getChild(layersList[layerIndex]); keyList = util.getObjectProperties(currentLayer, true, false); //update values for currentLayer if(tl_keys.length){ for(var i=0; i<tl_keys.length; i++){ var currentKey = currentLayer.getChild(keyList[i]); currentKey.setName(tl_keys[i].name); currentKey.time.set(tl_keys[i].position.get()); currentKey.value.set(tl_keys[i].value.get()); } } } } inibValuesChanged = false; } function struct_exportToFile(){} // function rebuildValuesStructure() { // script.log("updating values structure"); // //empty current structure // // local.values.removeAll(); // local.getChild("Values").removeContainer("newGroup"); // local.getChild("Values").removeContainer("groups"); // local.getChild("Values").removeContainer("ungrouped"); // //rebuild containers // var ng_cont = local.values.addContainer("New group"); // var g_cont = local.values.addContainer("groups"); // var ug_cont = local.values.addContainer("ungrouped"); // // rebuild new group // ng_cont.addTrigger("add group", "adds a new group at cursor time with below values"); // ng_cont.addFloatParameter("group time", "time position where the group will be created ", 0., 0.); // var layer_cont = ng_cont.addContainer("layers values"); // var layers = util.getObjectProperties(sequence_structure.keys.layers) ; // for (var i=0; i<layers.length; i++){ // //add a float value in each layer for group creation // layer_cont.addFloatParameter(layers[i], "", 0., 0., 1.); // ug_cont.addContainer(layers[i]); // } // //rebuild keys grouped/ungrouped // //first create group containers in groups // // //then create layer containers in ungrouped // // var layer_cont = ug_cont.addContainer("layers values"); // } /////////////////////// TIMELINE FUNCTIONS /////////////////////// function seq_clearSequence(){ var seq = root.sequences.getItemWithName(sequence) ; script.log("clearing all keys and cues of sequence : "+seq.name); seq.cues.removeAll(); var layers = seq.layers.getItems(); if(layers.length){ for (var i = 0; i<layers.length; i++){ layers[i].automation.removeAll(); } } } //use Values structure to rebuild the sequence function seq_buildSeqFromValues(){} //use file to rebuild sequence function seq_buildSeqFromFile(){} // function refreshKeysList(){ // var sequence = root.sequences.getItemWithName(sequence_structure.params.sequence); // var cues = sequence.cues.getItems(); // sequence_structure.keys.cues = {}; // if(cues.length){ // for (var i = 0 ; i < cues.length ; i++){ // sequence_structure.keys.cues['cue'+i] = {index : i}; // sequence_structure.keys['cues']['cue'+i]['name'] = cues[i].name; // sequence_structure.keys['cues']['cue'+i]['niceName'] = cues[i].niceName; // sequence_structure.keys['cues']['cue'+i]['time'] = cues[i].time.get(); // // script.log(tl_keyframes['cues']['cue'+i].time); // } // } // var layers = sequence.layers.getItems(); // sequence_structure.keys.layers={}; // if(layers.length){ // for (var i = 0 ; i < layers.length ; i++){ // sequence_structure['keys']['layers'][layers[i].name] = {}; // // script.log(currentLayer.name); // if(layers[i].automation.getItems().length){ // // script.log(currentLayer.automation.items.length); // for (var j = 0 ; j < layers[i].automation.getItems().length ; j++){ // var currentkey = layers[i].automation.getItems()[j] ; // sequence_structure['keys']['layers'][layers[i].name][currentkey.name] = { // position : currentkey.position.get(), // value : currentkey.value.get(), // name : currentkey.name, // niceName : currentkey.niceName, // //la propriete "group" est écrite // group : currentkey.group // // group : currentkey.hasChild("group") != undefined ? currentkey.group.get() : "ungrouped" // }; // // script.log(tl_keyframes['keys'][currentLayer.name][currentkey.name].value); // } // } // } // } // // myFile.writeFile(JSON.stringify(tl_keyframes), 1); // } // function writeKeyGroup(){ // var groups = sequence_structure.groups // } function createNewGroup(){ script.log("create new group"); //create a cue and a key in each layer at cursor position in selected sequence groups = local.getChild("values").groups; var groupsList = util.getObjectProperties(groups, true, false); var index = groupsList.length == undefined ? 0 : groupsList.length; var currentGroup = groups.addContainer("Group"+ index); var tl_seq = root.sequences.getItemWithName(sequence); var tl_layers = tl_seq.layers.getItems(); script.log(groupList.length); //Get current sequence time position and create cue at this position, with group name var currentTime = tl_seq.currentTime.get() ; // var currentTime = local.values.newGroup.groupTime.get(); script.log(currentTime); newCue = tl_seq.cues.addItem(); newCue.time.set(currentTime); newCue.setName(currentGroup.name); currentGroup.addFloatParameter(newCue.name, "cue of the group", currentTime, 0.); // var struc_layers = util.getObjectProperties(sequence_structure.keys.layers); if(tl_layers.length){ for(var i=0;i<tl_layers.length; i++ ){ if(tl_layers[i].automation!=undefined){//only for mapping layers // script.log(sequence_structure.keys.layers[layers[i].name]); var newKey = tl_layers[i].automation.addItem(); newKey.position.set(currentTime); // newKey.value.set(local.values.newGroup.layersValues[layers[i].name].get()); newKey.value.set(Math.random()); currentGroup.addFloatParameter(tl_layers[i].name,tl_layers[i].name+" value" , newKey.value.get(), 0.); // newKey.addStringParameter("group", "key is part of this group","mongroupe"); tl_layers[i].automation.reorderItems(); } } } // seq = seq.getItems(); }