improved cueList data structure magic

This commit is contained in:
sparks-alec
2023-06-11 15:18:39 -04:00
parent c61b189634
commit 0444d3976e
4 changed files with 155 additions and 103 deletions
+41 -25
View File
@@ -2,7 +2,7 @@
<% let playbackPosition = workspace.playbackPosition==cue.uniqueID ? "playback-position" : "" %>
<% let selected = workspace.selected.includes(cue.uniqueID) ? "selected": "" %>
<% if(cueKeys && cueKeys.parent && cueKeys.nestedGroupPosition){ %>
<% if(cueKeys && cueKeys.parent){ %>
<tr class="q-<%= cueKeys.colorName %> q-armed-<%= cueKeys.armed %> <%= playbackPosition %> <%= selected %>" id="<%= cueKeys.uniqueID %>">
@@ -48,38 +48,54 @@
<td><center><%= cueKeys.number || "&nbsp;" %></center></td>
<% let nextCue = cueKeys.cueInWorkspace.parent.cues[cueKeys.cueInWorkspace.sortIndex+1]; %>
<!-- spaces before name -->
<% for(let i=0; i<cueKeys.nestedGroupModes.length; i++){ %>
<% for(let i=0; i<cueKeys.parentKeys.length; i++){ %>
<% let parent = cueKeys.parentKeys[i+1]; %>
<% %>
<% if(cue.type=="Group" && cue.cues.length==0 && i==cueKeys.nestedGroupModes.length-1){ %>
<td class="gMode-<%= cueKeys.nestedGroupModes[i] %> gLeft gBot gTop group-arrow"></td>
<% }else if(cue.type=="Group" && i==cueKeys.nestedGroupModes.length-1){ %>
<td class="gMode-<%= cueKeys.mode %> gLeft gTop group-arrow"></td>
<% if(cueKeys.type=="Group" && cueKeys.cues.length==0 && i==cueKeys.parentKeys.length-1){ %>
<td class="gMode-<%= cueKeys.mode %> gLeft gTop gBot group-arrow"></td>
<% }else if(cueKeys.nestedGroupPosition[i]<=0){ %>
<% if(cueKeys.nestedGroupPosition.slice(i+1).every(v => v === 0)){ %>
<td class="gMode-<%= cueKeys.nestedGroupModes[i] %> gLeft gBot"></td>
<% }else if(cueKeys.type=="Group" && i==cueKeys.parentKeys.length-1){ %>
<td class="gMode-<%= cueKeys.mode %> gLeft gTop group-arrow"></td>
<%}else if(parent){%>
<% let parentCues = parent.cueInWorkspace.cues %>
<% let lastCueInMe = parentCues[parentCues.length-1]%>
<% while(lastCueInMe.type=="Group" && lastCueInMe.cues.length){
parentCues = lastCueInMe.cues;
lastCueInMe = parentCues[parentCues.length-1];
} %>
<% if(lastCueInMe.uniqueID == cueKeys.uniqueID){ %>
<td class="gMode-<%= parent.mode %> gLeft gBot">x</td>
<% }else{ %>
<td class="gMode-<%= cueKeys.nestedGroupModes[i] %> gLeft"></td>
<td class="gMode-<%= parent.mode %> gLeft"></td>
<% } %>
<% }else if(i<cueKeys.nestedGroupModes.length-1){ %>
<td class="gMode-<%= cueKeys.nestedGroupModes[i] %> gLeft"></td>
<% }else if(cueKeys.nestedGroupPosition[i]==undefined && cueKeys.nestedGroupPosition[i-1]==0){ %>
<td class="gMode-<%= cueKeys.nestedGroupModes[i] %> gBot"></td>
<% }else if(cueKeys.parentKeys[i]){ %>
<% let nextCue2 = cueKeys.parentKeys[i].cues[cueKeys.cueInWorkspace.sortIndex+1]; %>
<% if(!nextCue2){ %>
<td class="gMode-<%= cueKeys.parentKeys[i].mode %> gBot"></td>
<% }else{ %>
<td></td>
<% } %>
<% }else{ %>
<td class="gMode-<%= cueKeys.nestedGroupModes[i] %>"></td>
<% } %>
<% } %>
<% } %>
@@ -87,19 +103,19 @@
<% if(cue.type=="Group"){ %>
<% if(cue.cues.length==0){ %>
<td id="cue-name" colspan="<%= 11-cueKeys.nestedGroupModes.length %>" class="gMode-<%= cueKeys.mode %> gTop gBot gRight"><%= cueKeys.listName %></td>
<% if(cue.cues && cue.cues.length==0){ %>
<td id="cue-name" colspan="<%= 11-cueKeys.parentKeys.length %>" class="gMode-<%= cueKeys.mode %> gTop gBot gRight"><%= cueKeys.listName %></td>
<% }else{ %>
<td id="cue-name" colspan="<%= 11-cueKeys.nestedGroupModes.length %>" class="gMode-<%= cueKeys.mode %> gTop gRight"><%= cueKeys.listName %></td>
<td id="cue-name" colspan="<%= 11-cueKeys.parentKeys.length %>" class="gMode-<%= cueKeys.mode %> gTop gRight"><%= cueKeys.listName %></td>
<% } %>
<% }else if(cueKeys.nestedGroupPosition[cueKeys.nestedGroupModes.length-2]==0){ %>
<td colspan="<%= 11-cueKeys.nestedGroupModes.length %>" class="cue-name gMode-<%= cueKeys.nestedGroupModes[cueKeys.nestedGroupModes.length-2] %> gBot gRight"><%= cueKeys.listName %></td>
<% }else if(!nextCue){ %>
<td colspan="<%= 11-cueKeys.parentKeys.length %>" class="cue-name gMode-<%= cueKeys.parent.mode %> gBot gRight"><%= cueKeys.listName %></td>
<% }else{ %>
<td colspan="<%= 11-cueKeys.nestedGroupModes.length %>" class="cue-name gMode-<%= cueKeys.nestedGroupModes[cueKeys.nestedGroupModes.length-2] %> gRight"><%= cueKeys.listName %></td>
<td colspan="<%= 11-cueKeys.parentKeys.length %>" class="cue-name gMode-<%= cueKeys.parent.mode %> gRight"><%= cueKeys.listName %></td>
<% } %>
+1 -1
View File
@@ -6,8 +6,8 @@
<th width="20px" style="border-left: none; border-right: none;"></th>
<th width="20px" style="border-left: none;"></th>
<th width="55px">Number</th>
<th width="25px" style="padding: 0px"></th>
<th width="25px" class="no-border">Name</th>
<th width="25px" style="padding: 0px"></th>
<th width="25px" class="no-border"></th>
<th width="25px" class="no-border"></th>
<th width="25px" class="no-border"></th>
+111 -75
View File
@@ -100,9 +100,7 @@ exports.data = function data(_device, oscData) {
device.send(`/workspace/${msgAddr[2]}/updates`, [{ type: 'i', value: 1 }]);
}
} else if (/reply\/workspace\/.*\/cueLists/.test(oscData.address)) {
device.data.workspaces[msgAddr[2]].cueLists = json.data;
processCueList(device.data.workspaces[msgAddr[2]].cueLists, Object.keys(device.data.cueKeys), device, []);
device.draw();
insertChildCues(device, device.data.workspaces[msgAddr[2]].cueLists, json.data, '[root group of cue lists]');
} else if (/reply\/cue_id\/.*\/valuesForKeys/.test(oscData.address)) {
const keyValues = json.data;
let cue = device.data.cueKeys[msgAddr[2]];
@@ -143,29 +141,22 @@ exports.data = function data(_device, oscData) {
device.data.lastElapsedMessage = device.data.ticks;
}
let testCue = device.data.cueKeys[msgAddr[2]];
let nestedGroupModes;
if (cue.type === 'Group') {
nestedGroupModes = [testCue.mode];
} else if (cue.parent !== '[root group of cue lists]') {
nestedGroupModes = [device.data.cueKeys[cue.parent].mode];
if (cue.parent !== '[root group of cue lists]') {
cue.parent = device.data.cueKeys[cue.parent];
}
while (testCue.parent !== '[root group of cue lists]') {
testCue = device.data.cueKeys[testCue.parent];
if (testCue === undefined) {
break;
}
nestedGroupModes.unshift(testCue.mode);
cue.nestedGroupModes = nestedGroupModes;
}
if (cue.type === 'Cue List' || cue.type === 'Group' || cue.type === 'Cart') {
if (device.data.cueKeys[msgAddr[2]].cueInWorkspace) {
device.data.cueKeys[msgAddr[2]].cueInWorkspace.cues = [...keyValues.children];
if (device.data.cueKeys[msgAddr[2]].cueInWorkspace && cue.cues) {
insertChildCues(
device,
device.data.cueKeys[msgAddr[2]].cueInWorkspace.cues,
[...keyValues.children],
device.data.cueKeys[msgAddr[2]].cueInWorkspace
);
}
device.draw();
}
if (cue.type !== 'Cue List' && cue.type !== 'Cart') {
device.update('updateCueRow', { cue, workspace: device.data.workspaces[json.workspace_id] });
}
@@ -201,24 +192,8 @@ exports.data = function data(_device, oscData) {
workspace.selected.push(json.data[i].uniqueID);
}
device.update('updatePlaybackAndSelected', { workspace: device.data.workspaces[json.workspace_id] });
} else if (/reply\/(workspace\/.*\/)?cue_id\/(.*)\/children/.test(oscData.address)) {
// qlab 4 leaves off the workspace/<workspace_id> portion of the address this regex handles that
const addressMatch = oscData.address.match(/reply\/(workspace\/.*\/)?cue_id\/(.*)\/children/);
// trying to use .cueInWorkspace to reference the related cue in data.workspaces
device.data.cueKeys[addressMatch[2]].cueInWorkspace.cues = [...json.data];
console.log(device.data.cueKeys[addressMatch[2]].cueInWorkspace.cues);
device.draw();
} else if (/update\/workspace\/.*\/cue_id\/.*/.test(oscData.address)) {
if (device.data.cueKeys[msgAddr[4]] && device.data.cueKeys[msgAddr[4]].type === 'Group') {
device.send(`/workspace/${msgAddr[2]}/cue_id/${msgAddr[4]}/children/`);
} else if (device.data.cueKeys[msgAddr[4]] && device.data.cueKeys[msgAddr[4]].type === 'Cue List') {
device.send(`/workspace/${msgAddr[2]}/cueLists`);
} else if (device.data.cueKeys[msgAddr[4]] && device.data.cueKeys[msgAddr[4]].type === 'Cart') {
device.send(`/workspace/${msgAddr[2]}/cueLists`);
} else {
device.send(`/cue_id/${msgAddr[4]}/valuesForKeys`, [{ type: 's', value: valuesForKeysString }]);
}
device.send(`/cue_id/${msgAddr[4]}/valuesForKeys`, [{ type: 's', value: valuesForKeysString }]);
} else if (/update\/workspace\/.*\/cueList\/.*\/playbackPosition/.test(oscData.address)) {
const workspace = device.data.workspaces[msgAddr[2]];
if (workspace) {
@@ -231,46 +206,12 @@ exports.data = function data(_device, oscData) {
}
};
function processCueList(list, knownCueIDs, _device, _nestedIndex) {
const nestedIndex = _nestedIndex;
const device = _device;
for (let i = 0; i < list.length; i++) {
const cue = list[i];
nestedIndex[nestedIndex.length - 1] = list.length - i - 1;
if (!knownCueIDs.includes(cue.uniqueID)) {
device.send(`/cue_id/${cue.uniqueID}/valuesForKeys`, [{ type: 's', value: valuesForKeysString }]);
}
if (cue.cues?.length > 0) {
const nest2 = [..._nestedIndex];
nest2.push(cue.cues.length);
processCueList(cue.cues, knownCueIDs, device, nest2);
for (let j = 0; j < nestedIndex.length; j++) {
nestedIndex[j]++;
}
}
if (!knownCueIDs.includes(cue.uniqueID)) {
device.data.cueKeys[cue.uniqueID] = {
nestedGroupPosition: [...nestedIndex],
};
} else {
device.data.cueKeys[cue.uniqueID].nestedGroupPosition = [...nestedIndex];
}
// working theory is that list[i] is a new object rather pointing to the object within data.workspaces
device.data.cueKeys[cue.uniqueID].cueInWorkspace = list[i];
}
}
exports.update = function update(device, _doc, updateType, data) {
const doc = _doc;
if (updateType === 'updateCueRow') {
const $elem = doc.getElementById(data.cue.uniqueID);
if ($elem) {
if (device.data.cueKeys[data.cue.parent].type === 'Cart') {
if (data.cue.parent.type === 'Cart') {
$elem.outerHTML = device.templates.tile({
allCues: device.data.cueKeys,
cue: data.cue,
@@ -311,7 +252,7 @@ exports.update = function update(device, _doc, updateType, data) {
}
const cue = device.data.cueKeys[data.workspace.playbackPosition];
if (cue) {
if (cue && $playheadName) {
$playheadName.setAttribute('class', `playhead-name cartColor-${cue.colorName} playhead-disarmed`);
$playheadInfo.classList.add('playhead-active');
if (cue.number) {
@@ -339,7 +280,8 @@ exports.update = function update(device, _doc, updateType, data) {
}
};
exports.heartbeat = function heartbeat(device) {
exports.heartbeat = function heartbeat(_device) {
const device = _device;
device.data.ticks++;
if (device.data.ticks % 10 === 0) {
device.send(`/thump`);
@@ -350,3 +292,97 @@ exports.heartbeat = function heartbeat(device) {
device.send(`/cue_id/active/actionElapsed`);
}
};
function insertChildCues(_device, _oldCues, newCues, _parent) {
const device = _device;
const newIDs = _.keyBy(newCues, 'uniqueID');
const oldCues = _oldCues;
const parent = _parent;
if (!oldCues) {
return;
}
for (let i = 0; i < oldCues.length; i++) {
const oldCue = oldCues[i];
if (!newIDs[oldCue.uniqueID]) {
oldCues.splice(i, 1);
i--;
}
const indexOfOldCueInNew = _.findIndex(newCues, (o) => o.uniqueID === oldCue.uniqueID);
if (indexOfOldCueInNew >= 0) {
const newCue = newCues[indexOfOldCueInNew];
oldCues[i].armed = newCue.armed;
oldCues[i].colorName = newCue.colorName;
oldCues[i].colorNameLive = newCue['colorName/live'];
oldCues[i].flagged = newCue.flagged;
oldCues[i].listName = newCue.listName;
oldCues[i].name = newCue.name;
oldCues[i].number = newCue.number;
oldCues[i].type = newCue.type;
oldCues[i].sortIndex = indexOfOldCueInNew;
oldCues[i].parent = parent;
newCue.used = true;
let keys = device.data.cueKeys[oldCues[i].uniqueID];
if (!keys) {
device.data.cueKeys[oldCues[i].uniqueID] = {};
keys = device.data.cueKeys[oldCues[i].uniqueID];
}
keys.cueInWorkspace = oldCues[i];
if (oldCues[i].type === 'Group' || oldCues[i].type === 'Cue List' || oldCues[i].type === 'Cart') {
insertChildCues(device, oldCues[i].cues, newCue.cues, oldCues[i]);
}
}
}
for (let i = 0; i < newCues.length; i++) {
const newCue = newCues[i];
if (newCue.used !== true) {
const newIndex = oldCues.push({
armed: newCue.armed,
colorName: newCue.colorName,
colorNameLive: newCue['colorName/live'],
flagged: newCue.flagged,
listName: newCue.listName,
name: newCue.name,
number: newCue.number,
type: newCue.type,
uniqueID: newCue.uniqueID,
sortIndex: i,
parent,
});
const freshCue = oldCues[newIndex - 1];
let keys = device.data.cueKeys[freshCue.uniqueID];
if (!keys) {
device.data.cueKeys[freshCue.uniqueID] = {};
keys = device.data.cueKeys[freshCue.uniqueID];
}
if (freshCue.parent === '[root group of cue lists]') {
keys.parentKeys = [];
} else {
keys.parentKeys = [...device.data.cueKeys[freshCue.parent.uniqueID].parentKeys];
keys.parentKeys.push(device.data.cueKeys[freshCue.parent.uniqueID]);
}
device.send(`/cue_id/${newCue.uniqueID}/valuesForKeys`, [{ type: 's', value: valuesForKeysString }]);
keys.cueInWorkspace = freshCue;
if (freshCue.type === 'Group' || freshCue.type === 'Cue List' || oldCues[i].type === 'Cart') {
freshCue.cues = [];
insertChildCues(device, freshCue.cues, newCue.cues, freshCue);
}
}
}
oldCues.sort((a, b) => a.sortIndex - b.sortIndex);
}
+2 -2
View File
@@ -7,8 +7,8 @@
let parent = allCues[cue.parent];
if(cueKeys.parent){
width = 100/allCues[cueKeys.parent].cartColumns;
height = 600/allCues[cueKeys.parent].cartRows;
width = 100/cueKeys.parent.cartColumns;
height = 600/cueKeys.parent.cartRows;
top = (cueKeys.cartPosition[0]-1)*height;
left = (cueKeys.cartPosition[1]-1)*width;