1029 lines
25 KiB
TypeScript
1029 lines
25 KiB
TypeScript
'use strict';
|
|
|
|
/** @module lib/mscript */
|
|
|
|
|
|
interface RGB extends Array<number>{
|
|
[index : number] : number;
|
|
}
|
|
|
|
const BLACK : string = '0,0,0';
|
|
const WHITE : string = '255,255,255';
|
|
const CMD : string[] = [
|
|
'CF',
|
|
'PF',
|
|
'BF',
|
|
'CB',
|
|
'PB',
|
|
'BB',
|
|
'C2F',
|
|
'C2B',
|
|
'CCF',
|
|
'CCB',
|
|
'P2F',
|
|
'P2B',
|
|
'PPF',
|
|
'PPB',
|
|
'CFCB',
|
|
'CBCF',
|
|
'PFPB',
|
|
'PBPF'
|
|
];
|
|
|
|
const CAMERA_SECONDARY : string[] = [
|
|
'C2F',
|
|
'C2B',
|
|
'CCF',
|
|
'CCB',
|
|
'CFCB',
|
|
'CBCF'
|
|
];
|
|
|
|
const PROJECTOR_SECONDARY : string[] = [
|
|
'P2F',
|
|
'P2B',
|
|
'PPF',
|
|
'PPB',
|
|
'PFPB',
|
|
'PBPF'
|
|
];
|
|
|
|
const ALTS : any = {
|
|
'CF' : ['CAMERA FORWARD', 'CAM FORWARD'],
|
|
'PF' : ['PROJECTOR FORWARD', 'PROJ FORWARD'],
|
|
'BF' : ['BLACK FORWARD', 'BLACK', 'BLANK FORWARD', 'BLANK'],
|
|
'CB' : ['CAMERA BACKWARD', 'CAM BACKWARD', 'CAMERA BACK', 'CAM BACK'],
|
|
'PB' : ['PROJECTOR FORWARD', 'PROJ FORWARD', 'PROJECTOR BACK', 'PROJ BACK'],
|
|
'BB' : ['BLACK BACKWARD', 'BLACK BACK', 'BLANK BACK'],
|
|
'L ' : ['LIGHT', 'COLOR', 'LAMP'],
|
|
'F ' : ['FADE'],
|
|
'C2F' : ['CAMERA2 FORWARD', 'CAM2 FORWARD'],
|
|
'C2B' : ['CAMERA2 BACKWARD', 'CAM2 BACKWARD', 'CAMERA2 BACK', 'CAM2 BACK'],
|
|
'CCF' : ['CAMERAS FORWARD', 'CAMS FORWARD'],
|
|
'CCB' : ['CAMERAS BACKWARD', 'CAMS BACKWARD', 'CAMERAS BACK', 'CAMS BACK'],
|
|
'P2F' : ['PROJECTOR2 FORWARD', 'PROJ2 FORWARD'],
|
|
'P2B' : ['PROJECTOR2 BACKWARD', 'PROJ2 BACKWARD', 'PROJECTOR2 BACK', 'PROJ2 BACK'],
|
|
'PPF' : ['PROJECTORS FORWARD', 'PROJS FORWARD'],
|
|
'PPB' : ['PROJECTORS BACKWARD', 'PROJS BACKWARD', 'PROJECTORS BACK', 'PROJS BACK'],
|
|
|
|
'CFCB' : [ ],
|
|
'CBCF' : [ ],
|
|
'PFPB' : [ ],
|
|
'PBPF' : [ ]
|
|
};
|
|
|
|
const DELAY : string = 'DELAY';
|
|
const PAUSE : string = 'PAUSE';
|
|
const ALERT : string = 'ALERT';
|
|
|
|
/** helper functions */
|
|
|
|
/** startswith function from lodash, do not want the entire lib for this
|
|
* @param str {string} Text to evaluate
|
|
* @param target {string} Text to compare string against
|
|
* @param position {integer} Position in the string to make comparison at
|
|
*
|
|
* @returns {boolean} True for match, false for no match
|
|
**/
|
|
function startsWith (str : string, target : string, position? : number) : boolean {
|
|
const { length } = str;
|
|
|
|
position = position == null ? 0 : position;
|
|
|
|
if (position < 0) {
|
|
position = 0;
|
|
} else if (position > length) {
|
|
position = length;
|
|
}
|
|
|
|
target = `${target}`;
|
|
|
|
return str.slice(position, position + target.length) == target;
|
|
}
|
|
|
|
/** class Mscript */
|
|
export default class Mscript {
|
|
output : any;
|
|
lines : string[];
|
|
cam : number;
|
|
cam2 : number;
|
|
proj : number;
|
|
proj2 : number;
|
|
color : string;
|
|
loops : any[];
|
|
rec : number;
|
|
two : string;
|
|
three : string;
|
|
four : string;
|
|
arr : any[];
|
|
meta : string[];
|
|
target : number;
|
|
dist : number;
|
|
variables : any;
|
|
|
|
/**
|
|
* @constructor
|
|
* Create new Mscript interpreter
|
|
**/
|
|
|
|
constructor () {
|
|
this.output = {};
|
|
}
|
|
/**
|
|
* Clear the state of the script
|
|
*/
|
|
clear () {
|
|
this.lines = [];
|
|
|
|
this.cam = 0;
|
|
this.cam2 = 0;
|
|
this.proj = 0;
|
|
this.proj2 = 0;
|
|
this.color = '';
|
|
this.loops = [];
|
|
this.rec = -1;
|
|
this.two = '';
|
|
this.three = '';
|
|
this.four = '';
|
|
this.arr = [];
|
|
this.meta = [];
|
|
this.target = 0; //move to target using CAM # or PROJ #
|
|
this.dist = 0;
|
|
|
|
this.variables = {};
|
|
|
|
this.output = {};
|
|
}
|
|
/**
|
|
* Main function, accepts multi-line string, parses into lines
|
|
* and interprets the instructions from the text. Returns an array
|
|
* of steps to be fed into the mcopy sequence.
|
|
*
|
|
* @param {string} text Mscript text to interpret
|
|
* @param {function} callback Function to call when string is interpreted
|
|
*
|
|
* @returns {object} if callback is not provided
|
|
*/
|
|
interpret (text : string, callback : Function = null) {
|
|
this.clear()
|
|
|
|
if (typeof text === 'undefined') {
|
|
return this.fail('No input');
|
|
}
|
|
|
|
//split string into lines, each containing a command
|
|
this.lines = text.split('\n');
|
|
|
|
this.lines = this.lines.map(line => {
|
|
line = line.replace(/\t+/g, ''); //strip tabs
|
|
line = line.trim(); //remove excess whitespace before and after command
|
|
line = line.toUpperCase();
|
|
return line;
|
|
})
|
|
|
|
for (let line of this.lines) {
|
|
this.two = line.substring(0, 2);
|
|
this.three = line.substring(0, 3);
|
|
this.four = line.substring(0, 4);
|
|
if (CMD.indexOf(this.four) !== -1) {
|
|
this.basic_cmd(line, this.four);
|
|
} else if (CMD.indexOf(this.three) !== -1) {
|
|
this.basic_cmd(line, this.three);
|
|
} else if (CMD.indexOf(this.two) !== -1) {
|
|
this.basic_cmd(line, this.two);
|
|
} else if (startsWith(line, DELAY)) {
|
|
this.delay(line);
|
|
} else if (startsWith(line, PAUSE)) {
|
|
this.pause(line);
|
|
} else if (startsWith(line, ALERT)) {
|
|
this.alert(line);
|
|
} else if (startsWith(line, '@') || line.indexOf('@') !== -1) {
|
|
this.variable(line);
|
|
} else if (startsWith(line, 'LOOP')) {
|
|
this.new_loop(line);
|
|
} else if (startsWith(line, 'L ')) {
|
|
this.light_state(line);
|
|
} else if (startsWith(line, 'F ')) {
|
|
this.new_loop(line, true);
|
|
} else if (startsWith(line, 'END')) {
|
|
this.end_loop(line);
|
|
} else if (startsWith(line, 'CAM2')) { //directly go to that frame
|
|
this.move_cam2(line);
|
|
} else if (startsWith(line, 'CAM')) { //directly go to that frame
|
|
this.move_cam(line);
|
|
} else if (startsWith(line, 'PROJ2')) { //directly go to that frame
|
|
this.move_proj2(line);
|
|
} else if (startsWith(line, 'PROJ')) { //directly go to that frame
|
|
this.move_proj(line);
|
|
} else if (startsWith(line, 'SET')) { //set that state
|
|
this.set_state(line);
|
|
} else if (startsWith(line, '#') || startsWith(line, '//')) {
|
|
//comments
|
|
//ignore while parsing
|
|
}
|
|
}
|
|
|
|
this.output.success = true;
|
|
this.output.arr = this.arr; //all instructions
|
|
this.output.meta = this.meta; //all metadata for instructions
|
|
this.output.cam = this.cam;
|
|
this.output.proj = this.proj;
|
|
if (this.contains(this.arr, CAMERA_SECONDARY)) {
|
|
this.output.cam2 = this.cam2;
|
|
}
|
|
if (this.contains(this.arr, PROJECTOR_SECONDARY)) {
|
|
this.output.proj2 = this.proj2;
|
|
}
|
|
|
|
if (typeof callback !== 'undefined' && callback != null) {
|
|
//should only be invoked by running mscript.tests()
|
|
callback(this.output);
|
|
} else {
|
|
return this.output;
|
|
}
|
|
}
|
|
/**
|
|
* Interprets variables for complex sequence behavior.
|
|
* TODO: Fully implement, add test coverage
|
|
*
|
|
* @param {string} line Line containing a variable assignment
|
|
*
|
|
**/
|
|
variable (line : string) {
|
|
let parts : string[] = line.split('=');
|
|
let key : string = parts[0];
|
|
let value : any = parts[1];
|
|
let update : boolean = false;
|
|
|
|
if (value && value.indexOf('#') !== -1) {
|
|
value = value.split('#')[0];
|
|
}
|
|
|
|
if (value && value.indexOf('//') !== -1) {
|
|
value = value.split('//')[0];
|
|
}
|
|
|
|
if (value && value.indexOf('+') !== -1) {
|
|
if (value)
|
|
update = true;
|
|
}
|
|
|
|
if (line.indexOf('-') !== -1) {
|
|
update = true;
|
|
}
|
|
|
|
if (line.indexOf(',') === -1) { //if not color string
|
|
try {
|
|
value = parseInt(value);
|
|
} catch (err) {
|
|
//supress parsing error
|
|
}
|
|
}
|
|
//console.dir(parts)
|
|
if (!this.variables[key] || update) {
|
|
this.variables[key] = value;
|
|
}
|
|
//console.dir(this.variables)
|
|
}
|
|
|
|
/**
|
|
* Replace variable with value at time of interpretation
|
|
* TODO: Implement this please
|
|
*
|
|
* @param {string} line Line containing variable to be replaced with value
|
|
*
|
|
* @returns {string} New string to be interpreted
|
|
**/
|
|
variable_replace(line : string) : string {
|
|
return line;
|
|
}
|
|
/**
|
|
* Interpret a basic two character command
|
|
*
|
|
* @param {string} line Line of script to interpret
|
|
* @param {string} short The short command to use
|
|
*/
|
|
basic_cmd (line : string, short : string) {
|
|
if (this.rec !== -1) {
|
|
//hold generated arr in state loop array
|
|
this.loops[this.rec].arr
|
|
.push.apply(this.loops[this.rec].arr,
|
|
this.str_to_arr(line,
|
|
short));
|
|
this.loops[this.rec].meta
|
|
.push.apply(this.loops[this.rec].meta,
|
|
this.light_to_arr(line,
|
|
short));
|
|
} else {
|
|
this.arr.push.apply(this.arr, this.str_to_arr(line, short));
|
|
this.meta.push.apply(this.meta, this.light_to_arr(line, short));
|
|
}
|
|
}
|
|
/**
|
|
* Start a new loop
|
|
*
|
|
* @param {string} line Line to evaluate as either loop or fade
|
|
* @param {boolean} fade Flag as true if fade
|
|
*/
|
|
new_loop (line : string, fade? : boolean) {
|
|
this.rec++;
|
|
this.loops[this.rec] = {
|
|
arr : [],
|
|
meta : [],
|
|
cam : 0,
|
|
proj : 0,
|
|
cam2 : 0,
|
|
proj2 : 0,
|
|
cmd : line + ''
|
|
};
|
|
if (fade) {
|
|
this.fade(line);
|
|
}
|
|
}
|
|
/**
|
|
* Close the most recent loop
|
|
*
|
|
* @param {string} line Line to interpret
|
|
*/
|
|
end_loop (line : string) {
|
|
let meta_arr : string[];
|
|
let start : RGB;
|
|
let end : RGB;
|
|
let len : number;
|
|
|
|
for (let x = 0; x < this.loop_count(this.loops[this.rec].cmd); x++) {
|
|
meta_arr = this.loops[this.rec].meta;
|
|
if (this.loops[this.rec].fade) {
|
|
start = this.loops[this.rec].start;
|
|
end = this.loops[this.rec].end;
|
|
len = this.loops[this.rec].fade_len;
|
|
meta_arr = meta_arr.map(l => {
|
|
return this.fade_rgb(start, end, len, x);
|
|
})
|
|
}
|
|
if (this.rec === 0) {
|
|
this.arr.push.apply(this.arr, this.loops[this.rec].arr);
|
|
this.meta.push.apply(this.meta, meta_arr);
|
|
} else if (this.rec >= 1) {
|
|
this.loops[this.rec - 1].arr
|
|
.push.apply(this.loops[this.rec - 1].arr,
|
|
this.loops[this.rec].arr);
|
|
|
|
this.loops[this.rec - 1].meta
|
|
.push.apply(this.loops[this.rec - 1].meta,
|
|
meta_arr);
|
|
}
|
|
}
|
|
this.update('END', this.loop_count(this.loops[this.rec].cmd));
|
|
delete this.loops[this.rec];
|
|
this.rec--;
|
|
}
|
|
/**
|
|
* Move camera to explicitly-defined frame
|
|
*
|
|
* @param {string} line Line to interpret with camera move statement
|
|
*/
|
|
move_cam (line : string) {
|
|
this.target = parseInt(line.split('CAM ')[1]);
|
|
if (this.rec !== -1) {
|
|
if (this.target > this.cam) {
|
|
this.dist = this.target - this.cam;
|
|
for (let x = 0; x < this.dist; x++) {
|
|
this.loops[this.rec].arr.push('BF');
|
|
this.loops[this.rec].meta.push(BLACK);
|
|
this.update('BF');
|
|
}
|
|
} else {
|
|
this.dist = this.cam - this.target;
|
|
for (let x = 0; x < this.dist; x++) {
|
|
this.loops[this.rec].arr.push('BB');
|
|
this.loops[this.rec].meta.push(BLACK);
|
|
this.update('BB');
|
|
}
|
|
}
|
|
} else {
|
|
if (this.target > this.cam) {
|
|
this.dist = this.target - this.cam;
|
|
for (let x = 0; x < this.dist; x++) {
|
|
this.arr.push('BF');
|
|
this.meta.push(BLACK);
|
|
this.update('BF');
|
|
}
|
|
} else {
|
|
this.dist = this.cam - this.target;
|
|
for (let x = 0; x < this.dist; x++) {
|
|
this.arr.push('BB');
|
|
this.meta.push(BLACK);
|
|
this.update('BB');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Move secondary camera to explicitly-defined frame
|
|
*
|
|
* @param {string} line Line to interpret with camera move statement
|
|
*/
|
|
move_cam2 (line : string) {
|
|
this.target = parseInt(line.split('CAM2 ')[1]);
|
|
if (this.rec !== -1) {
|
|
if (this.target > this.cam2) {
|
|
this.dist = this.target - this.cam2;
|
|
for (let x = 0; x < this.dist; x++) {
|
|
this.loops[this.rec].arr.push('C2F');
|
|
this.loops[this.rec].meta.push(BLACK);
|
|
this.update('C2F');
|
|
}
|
|
} else {
|
|
this.dist = this.cam2 - this.target;
|
|
for (let x = 0; x < this.dist; x++) {
|
|
this.loops[this.rec].arr.push('C2B');
|
|
this.loops[this.rec].meta.push(BLACK);
|
|
this.update('C2B');
|
|
}
|
|
}
|
|
} else {
|
|
if (this.target > this.cam2) {
|
|
this.dist = this.target - this.cam2;
|
|
for (let x = 0; x < this.dist; x++) {
|
|
this.arr.push('C2F');
|
|
this.meta.push(BLACK);
|
|
this.update('C2F');
|
|
}
|
|
} else {
|
|
this.dist = this.cam2 - this.target;
|
|
for (let x = 0; x < this.dist; x++) {
|
|
this.arr.push('C2B');
|
|
this.meta.push(BLACK);
|
|
this.update('C2B');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Move projector to explicitly-defined frame
|
|
*
|
|
* @param {string} line Line containing `move` statement to interpret
|
|
*/
|
|
move_proj (line : string) {
|
|
this.target = parseInt(line.split('PROJ ')[1]);
|
|
if (this.rec !== -1) {
|
|
if (this.target > this.proj) {
|
|
this.dist = this.target - this.proj;
|
|
for (let x = 0; x < this.dist; x++) {
|
|
this.loops[this.rec].arr.push('PF');
|
|
this.loops[this.rec].meta.push('');
|
|
this.update('PF');
|
|
}
|
|
} else {
|
|
this.dist = this.proj - this.target;
|
|
for (let x = 0; x < this.dist; x++) {
|
|
this.loops[this.rec].arr.push('PB');
|
|
this.loops[this.rec].meta.push('');
|
|
this.update('PB');
|
|
}
|
|
}
|
|
} else {
|
|
if (this.target > this.proj) {
|
|
this.dist = this.target - this.proj;
|
|
for (let x = 0; x < this.dist; x++) {
|
|
this.arr.push('PF');
|
|
this.meta.push('');
|
|
this.update('PF');
|
|
}
|
|
} else {
|
|
this.dist = this.proj - this.target;
|
|
for (let x = 0; x < this.dist; x++) {
|
|
this.arr.push('PB');
|
|
this.meta.push('');
|
|
this.update('PB');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Move projector to explicitly-defined frame
|
|
*
|
|
* @param {string} line Line containing `move` statement to interpret
|
|
*/
|
|
move_proj2 (line : string) {
|
|
this.target = parseInt(line.split('PROJ2 ')[1]);
|
|
if (this.rec !== -1) {
|
|
if (this.target > this.proj2) {
|
|
this.dist = this.target - this.proj2;
|
|
for (let x = 0; x < this.dist; x++) {
|
|
this.loops[this.rec].arr.push('P2F');
|
|
this.loops[this.rec].meta.push('');
|
|
this.update('P2F');
|
|
}
|
|
} else {
|
|
this.dist = this.proj2 - this.target;
|
|
for (let x = 0; x < this.dist; x++) {
|
|
this.loops[this.rec].arr.push('P2B');
|
|
this.loops[this.rec].meta.push('');
|
|
this.update('P2B');
|
|
}
|
|
}
|
|
} else {
|
|
if (this.target > this.proj2) {
|
|
this.dist = this.target - this.proj2;
|
|
for (let x = 0; x < this.dist; x++) {
|
|
this.arr.push('P2F');
|
|
this.meta.push('');
|
|
this.update('P2F');
|
|
}
|
|
} else {
|
|
this.dist = this.proj2 - this.target;
|
|
for (let x = 0; x < this.dist; x++) {
|
|
this.arr.push('P2B');
|
|
this.meta.push('');
|
|
this.update('P2B');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Set the state of either the cam or projector
|
|
*
|
|
* @param line {string} String containing set statement
|
|
*/
|
|
set_state (line : string) {
|
|
if (startsWith(line, 'SET CAM2')) {
|
|
parseInt(line.split('SET CAM2')[1]);
|
|
} else if (startsWith(line, 'SET PROJ2')) {
|
|
this.cam2 = parseInt(line.split('SET PROJ2')[1]);
|
|
} else if (startsWith(line, 'SET CAM')) {
|
|
this.cam = parseInt(line.split('SET CAM')[1]);
|
|
} else if (startsWith(line, 'SET PROJ')) {
|
|
this.proj = parseInt(line.split('SET PROJ')[1]);
|
|
}
|
|
}
|
|
/**
|
|
* Return the last loop
|
|
*
|
|
* @returns {object}
|
|
*/
|
|
last_loop () : any {
|
|
return this.loops[this.loops.length - 1];
|
|
}
|
|
/**
|
|
* Return the second-last loop
|
|
*
|
|
* @returns {object} Loop array
|
|
*/
|
|
parent_loop () : any {
|
|
return this.loops[this.loops.length - 2];
|
|
}
|
|
/**
|
|
* Extract the loop count integer from a LOOP cmd
|
|
*
|
|
* @returns {integer} Loop count in string parsed into integer
|
|
*/
|
|
loop_count (str : string) : number {
|
|
return parseInt(str.split(' ')[1]);
|
|
}
|
|
/**
|
|
* Execute a fade of frame length, from color to another color
|
|
*
|
|
* @param {string} line Line containing a fade initiator
|
|
*/
|
|
fade (line : string) {
|
|
let len = this.fade_count(line);
|
|
let start = this.fade_start(line);
|
|
let end = this.fade_end(line);
|
|
|
|
this.loops[this.rec].start = start;
|
|
this.loops[this.rec].end = end;
|
|
this.loops[this.rec].fade = true;
|
|
this.loops[this.rec].fade_count = 0;
|
|
this.loops[this.rec].fade_len = len;
|
|
}
|
|
/**
|
|
* Extract the fade length integer from a FADE cmd
|
|
*
|
|
* @param {string} str Line containing the length of fade in frames
|
|
*/
|
|
fade_count (str : string) {
|
|
return parseInt(str.split(' ')[1]);
|
|
}
|
|
/**
|
|
* Extract the start color from a string
|
|
*
|
|
* @param {string} str Line containing the start color value in a fade initiator
|
|
*
|
|
* @returns {array} Array containing RGB color values
|
|
*/
|
|
fade_start (str : string) : RGB {
|
|
let color : string = str.split(' ')[2];
|
|
return this.rgb(color.trim())
|
|
}
|
|
/**
|
|
* Extract the end color from a string
|
|
*
|
|
* @param {string} str Line containing the end color value in a fade initiator
|
|
*
|
|
* @returns {array} Array containing RGB color values
|
|
*/
|
|
fade_end (str : string) : RGB {
|
|
let color : string = str.split(' ')[3];
|
|
return this.rgb(color.trim())
|
|
}
|
|
/**
|
|
* Determine the state of a fade at a particular frame in the sequence, x
|
|
*
|
|
* @param {array} start Color the fade starts at
|
|
* @param {array} end Color the fade finishes at
|
|
* @param {integer} len Total length of the fade in frames
|
|
* @param {integer} x Position of the fade to get color value of
|
|
*
|
|
* @returns {array} Array containing RGB color values
|
|
*/
|
|
fade_rgb (start : RGB, end : RGB, len : number, x : number) {
|
|
let cur = [];
|
|
let diff;
|
|
for (let i : number = 0; i < 3; i++) {
|
|
if (x === len - 1) {
|
|
cur[i] = end[i];
|
|
} else if (start[i] >= end[i]) {
|
|
diff = start[i] - end[i];
|
|
cur[i] = start[i] - Math.round((diff / (len - 1)) * x);
|
|
} else {
|
|
diff = end[i] - start[i];
|
|
cur[i] = start[i] + Math.round((diff / (len - 1)) * x);
|
|
}
|
|
}
|
|
return this.rgb_str(cur);
|
|
|
|
}
|
|
/**
|
|
* Parse string into array of RGB color values. 0-255 octet.
|
|
*
|
|
* @param {string} str String containing only color values as `#,#,#`
|
|
**/
|
|
rgb (str : string) : RGB {
|
|
let rgb = str.split(',');
|
|
return rgb.map( (char : string) => {
|
|
return parseInt(char);
|
|
})
|
|
}
|
|
/**
|
|
* Cast RGB color values as string
|
|
*
|
|
* @param {array} arr Array to join into string
|
|
*
|
|
* @returns {string} String of RGB values
|
|
**/
|
|
rgb_str (arr : RGB) : string {
|
|
return arr.join(',');
|
|
}
|
|
/**
|
|
* Increase the state of a specific object, such as the camera/projector,
|
|
* by the value defined in val.
|
|
*
|
|
* @param {string} cmd String representing command to interpret and update state
|
|
*/
|
|
update (cmd : string, val : number = 1) {
|
|
if (cmd === 'END') {
|
|
//I don't understand this loop
|
|
for (let i = 0; i < val; i++) {
|
|
if (this.rec === 0) {
|
|
this.cam += this.loops[this.rec].cam;
|
|
this.proj += this.loops[this.rec].proj;
|
|
} else if (this.rec >= 1) {
|
|
this.loops[this.rec - 1].cam += this.loops[this.rec].cam;
|
|
this.loops[this.rec - 1].proj += this.loops[this.rec].proj;
|
|
}
|
|
}
|
|
} else if (cmd === 'CF') {
|
|
if (this.rec === -1) {
|
|
this.cam += val;
|
|
} else {
|
|
this.loops[this.rec].cam += val;
|
|
}
|
|
} else if (cmd === 'CB') {
|
|
if (this.rec === -1) {
|
|
this.cam -= val;
|
|
} else {
|
|
this.loops[this.rec].cam -= val;
|
|
}
|
|
} else if (cmd === 'PF') {
|
|
if (this.rec === -1) {
|
|
this.proj += val;
|
|
} else {
|
|
this.loops[this.rec].proj += val;
|
|
}
|
|
} else if (cmd === 'PB') {
|
|
if (this.rec === -1) {
|
|
this.proj -= val;
|
|
} else {
|
|
this.loops[this.rec].proj -= val;
|
|
}
|
|
} else if (cmd === 'BF') {
|
|
if (this.rec === -1) {
|
|
this.cam += val;
|
|
} else {
|
|
this.loops[this.rec].cam += val;
|
|
}
|
|
} else if (cmd === 'BB') {
|
|
if (this.rec === -1) {
|
|
this.cam -= val;
|
|
} else {
|
|
this.loops[this.rec].cam -= val;
|
|
}
|
|
} else if (cmd === 'C2F') {
|
|
if (this.rec === -1) {
|
|
this.cam2 += val;
|
|
} else {
|
|
this.loops[this.rec].cam2 += val;
|
|
}
|
|
} else if (cmd === 'C2B') {
|
|
if (this.rec === -1) {
|
|
this.cam2 -= val;
|
|
} else {
|
|
this.loops[this.rec].cam2 -= val;
|
|
}
|
|
} else if (cmd === 'CCF') {
|
|
if (this.rec === -1) {
|
|
this.cam += val;
|
|
this.cam2 += val;
|
|
} else {
|
|
this.loops[this.rec].cam2 += val;
|
|
this.loops[this.rec].cam2 += val;
|
|
}
|
|
} else if (cmd === 'CCB') {
|
|
if (this.rec === -1) {
|
|
this.cam -= val;
|
|
this.cam2 -= val;
|
|
} else {
|
|
this.loops[this.rec].cam2 -= val;
|
|
this.loops[this.rec].cam2 -= val;
|
|
}
|
|
} else if (cmd === 'P2F') {
|
|
if (this.rec === -1) {
|
|
this.proj2 += val;
|
|
} else {
|
|
this.loops[this.rec].proj2 += val;
|
|
}
|
|
} else if (cmd === 'P2B') {
|
|
if (this.rec === -1) {
|
|
this.proj2 -= val;
|
|
} else {
|
|
this.loops[this.rec].proj2 -= val;
|
|
}
|
|
} else if (cmd === 'PPF') {
|
|
if (this.rec === -1) {
|
|
this.proj += val;
|
|
this.proj2 += val;
|
|
} else {
|
|
this.loops[this.rec].proj += val
|
|
this.loops[this.rec].proj2 += val;
|
|
}
|
|
} else if (cmd === 'PPB') {
|
|
if (this.rec === -1) {
|
|
this.proj -= val;
|
|
this.proj2 -= val;
|
|
} else {
|
|
this.loops[this.rec].proj -= val
|
|
this.loops[this.rec].proj2 -= val;
|
|
}
|
|
} else if (cmd === 'CFCB') {
|
|
if (this.rec === -1) {
|
|
this.cam += val;
|
|
this.cam2 -= val;
|
|
} else {
|
|
this.loops[this.rec].cam += val;
|
|
this.loops[this.rec].cam2 -= val;
|
|
}
|
|
} else if (cmd === 'CBCF') {
|
|
if (this.rec === -1) {
|
|
this.cam -= val;
|
|
this.cam2 += val;
|
|
} else {
|
|
this.loops[this.rec].cam -= val;
|
|
this.loops[this.rec].cam2 += val;
|
|
}
|
|
} else if (cmd === 'PFPB') {
|
|
if (this.rec === -1) {
|
|
this.proj += val;
|
|
this.proj2 -= val;
|
|
} else {
|
|
this.loops[this.rec].proj += val;
|
|
this.loops[this.rec].proj2 -= val;
|
|
}
|
|
} else if (cmd === 'PBPF') {
|
|
if (this.rec === -1) {
|
|
this.proj -= val;
|
|
this.proj2 += val;
|
|
} else {
|
|
this.loops[this.rec].proj -= val;
|
|
this.loops[this.rec].proj2 += val;
|
|
}
|
|
} else if (cmd === 'L ') {
|
|
|
|
}
|
|
}
|
|
/**
|
|
* Split string on command, turn into array of commands
|
|
* as long as count variable. Default 1.
|
|
*
|
|
* @param {string} str String to split
|
|
* @param {string} cmd String representing command to split at
|
|
*
|
|
* @returns {array} Array containing commands
|
|
*/
|
|
str_to_arr (str : string, cmd : string) : string[] {
|
|
const cnt : string[] = str.split(cmd);
|
|
let c : number = parseInt(cnt[1]);
|
|
let arr : any[] = [];
|
|
if (cnt[1] === '') {
|
|
c = 1;
|
|
} else {
|
|
c = parseInt(cnt[1]);
|
|
}
|
|
arr = new Array(c).fill(cmd);
|
|
this.update(cmd, c);
|
|
return arr;
|
|
}
|
|
/**
|
|
* Split a string on a command to extract data for light array
|
|
*
|
|
* @param {string} str String containing light command
|
|
* @param {string} cmd String representing command
|
|
*
|
|
* @returns {array} An RGB array containing the color values
|
|
*/
|
|
light_to_arr (str : string, cmd : string) : RGB {
|
|
const cnt : string[] = str.split(cmd);
|
|
let c : number = parseInt(cnt[1]);
|
|
let arr : any[] = [];
|
|
if (cnt[1] === '') {
|
|
c = 1;
|
|
} else {
|
|
c = parseInt(cnt[1]);
|
|
}
|
|
for (let i : number = 0; i < c; i++) {
|
|
if (cmd === 'CF'
|
|
|| cmd === 'CB') {
|
|
arr.push(this.color);
|
|
} else if (cmd === 'BF'
|
|
|| cmd === 'BB') {
|
|
arr.push(BLACK);
|
|
} else {
|
|
arr.push('');
|
|
}
|
|
}
|
|
return arr;
|
|
}
|
|
/**
|
|
* Split a string to extract an rgb color value
|
|
*
|
|
* @param {string} Color string assign to color property
|
|
*/
|
|
light_state (str : string) {
|
|
//add parsers for other color spaces
|
|
const color : string = str.replace('L ', '').trim();
|
|
this.color = color;
|
|
}
|
|
|
|
/**
|
|
* Interpret a delay command
|
|
*
|
|
* @param {string} line String containing delay command
|
|
**/
|
|
delay (line : string) {
|
|
let lenStr : string = line.split(' ')[1] || ''
|
|
let len : number;
|
|
lenStr = lenStr.trim();
|
|
|
|
try {
|
|
len = parseInt(lenStr, 10); //clean up string or fail
|
|
} catch (err) {
|
|
len = 0;
|
|
}
|
|
|
|
if (isNaN(len)) {
|
|
len = 0;
|
|
}
|
|
|
|
lenStr = String(len);
|
|
|
|
if (this.rec !== -1) {
|
|
//hold generated arr in state loop array
|
|
this.loops[this.rec].arr
|
|
.push('DE');
|
|
this.loops[this.rec].meta
|
|
.push(lenStr);
|
|
} else {
|
|
this.arr.push('DE');
|
|
this.meta.push(lenStr);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Interpret an alert command
|
|
*
|
|
* @param {string} line String containing alert message
|
|
**/
|
|
alert (line : string) {
|
|
let msg : string = line.split(' ')[1] || ''
|
|
msg = msg.trim();
|
|
if (this.rec !== -1) {
|
|
//hold generated arr in state loop array
|
|
this.loops[this.rec].arr
|
|
.push('AL');
|
|
this.loops[this.rec].meta
|
|
.push(line);
|
|
} else {
|
|
this.arr.push('AL');
|
|
this.meta.push(line);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Interpret an pause command
|
|
*
|
|
* @param {string} line String containing alert message
|
|
**/
|
|
pause (line : string) {
|
|
const msg : string = "Paused script. Click OK to continue."
|
|
if (this.rec !== -1) {
|
|
//hold generated arr in state loop array
|
|
this.loops[this.rec].arr
|
|
.push('AL');
|
|
this.loops[this.rec].meta
|
|
.push(msg);
|
|
} else {
|
|
this.arr.push('AL');
|
|
this.meta.push(msg);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Throw an error with specific message
|
|
*
|
|
* @param {string} msg Error message to print
|
|
*/
|
|
fail (msg : string) {
|
|
throw new Error(msg);
|
|
}
|
|
|
|
/**
|
|
* Determine if array contains matching elements of
|
|
* another array
|
|
*
|
|
* @param {Array} arr Original array to compare
|
|
* @param {Array} arr2 Array to compare elements from
|
|
*
|
|
* @returns {boolean} Whether arr contains elements in arr2
|
|
**/
|
|
contains (arr : string[], arr2 : string[]) {
|
|
return arr.some(r => arr2.includes(r));
|
|
}
|
|
}
|
|
|
|
module.exports = Mscript;
|
|
|
|
|
|
/*
|
|
|
|
CAM # - go to camera frame #
|
|
PROJ # - go to projector frame #
|
|
|
|
CAM2 # - go to camera 2 frame #
|
|
PROJ # - go to projector 2 frame #
|
|
|
|
SET CAM # - sets camera count to #
|
|
SET PROJ # - sets projector count to #
|
|
|
|
SET CAM2 # - sets camera 2 count to #
|
|
SET PROJ2 # - sets projector 2 count to #
|
|
|
|
LOOP # - begin loop, can nest recursively, # times
|
|
END LOOP - (or END) closes loop
|
|
|
|
L #RGB - sets light to rgb value
|
|
|
|
FADE 24 0,0,0 255,255,255
|
|
END FADE
|
|
|
|
CF - Camera forwards
|
|
PF - Projector forwards
|
|
BF - Black forwards
|
|
CB - Camera backwards
|
|
PB - Projector backwards
|
|
BB - Black backwards
|
|
C2F - Camera 2 forwards
|
|
C2B - Camera 2 backwards
|
|
CCF - Both cameras forwards
|
|
CCB - Both cameras backwards
|
|
P2F - Projector 2 forwards
|
|
P2B - Projector 2 backwards
|
|
PPF - Both projectors forwards
|
|
PPB - Both projectors backwards
|
|
|
|
ALERT {MESSAGE} - Stop the sequence and requiure user interaction to complete
|
|
|
|
*/ |