Compile mscript with Typescript

This commit is contained in:
mmcwilliams 2019-07-26 17:28:27 -04:00
parent 4b5dd4868b
commit 0b2d386ca2
6 changed files with 1617 additions and 1596 deletions

View File

@ -1,7 +1,4 @@
'use strict'; 'use strict';
/** @module lib/mscript */
const BLACK = '0,0,0'; const BLACK = '0,0,0';
const WHITE = '255,255,255'; const WHITE = '255,255,255';
const CMD = [ const CMD = [
@ -13,147 +10,151 @@ const CMD = [
'BB' 'BB'
]; ];
const ALTS = { const ALTS = {
'CF' : ['CAMERA FORWARD', 'CAM FORWARD'], 'CF': ['CAMERA FORWARD', 'CAM FORWARD'],
'PF' : ['PROJECTOR FORWARD', 'PROJ FORWARD'], 'PF': ['PROJECTOR FORWARD', 'PROJ FORWARD'],
'BF' : ['BLACK FORWARD', 'BLACK', 'BLANK FORWARD', 'BLANK'], 'BF': ['BLACK FORWARD', 'BLACK', 'BLANK FORWARD', 'BLANK'],
'CB' : ['CAMERA BACKWARD', 'CAM BACKWARD', 'CAMERA BACK', 'CAM BACK'], 'CB': ['CAMERA BACKWARD', 'CAM BACKWARD', 'CAMERA BACK', 'CAM BACK'],
'PB' : ['PROJECTOR FORWARD', 'PROJ FORWARD', 'PROJECTOR BACK', 'PROJ BACK'], 'PB': ['PROJECTOR FORWARD', 'PROJ FORWARD', 'PROJECTOR BACK', 'PROJ BACK'],
'BB' : ['BLACK BACKWARD', 'BLACK BACK', 'BLANK BACK'], 'BB': ['BLACK BACKWARD', 'BLACK BACK', 'BLANK BACK'],
'L ' : ['LIGHT', 'COLOR', 'LAMP'], 'L ': ['LIGHT', 'COLOR', 'LAMP'],
'F ' : ['FADE'] 'F ': ['FADE']
}; };
/** helper functions */ /** helper functions */
/** startswith function from lodash, do not want the entire lib for this
/** startswith function from lodash, do not want the entire lib for this */ * @param str {string} Text to evaluate
function startsWith(string, target, position) { * @param target {string} Text to compare string against
const { length } = string; * @param position {integer} Position in the string to make comparison at
*
* @returns {boolean} True for match, false for no match
**/
function startsWith(str, target, position) {
const { length } = str;
position = position == null ? 0 : position; position = position == null ? 0 : position;
if (position < 0) { if (position < 0) {
position = 0; position = 0;
} else if (position > length) { }
else if (position > length) {
position = length; position = length;
} }
target = `${target}`; target = `${target}`;
return string.slice(position, position + target.length) == target; return str.slice(position, position + target.length) == target;
} }
/** class Mscript */ /** class Mscript */
class Mscript { class Mscript {
constructor () { constructor() {
this.output = {}; this.output = {};
} }
/** /**
* Clear the state of the script * Clear the state of the script
*/ */
clear () { clear() {
this.lines = []; this.lines = [];
this.cam = 0; this.cam = 0;
this.proj = 0; this.proj = 0;
this.color = ''; this.color = '';
this.loops = []; this.loops = [];
this.rec = -1; this.rec = -1;
this.two = ''; this.two = '';
this.arr = []; this.arr = [];
this.light = []; this.light = [];
this.target = 0; //move to target using CAM # or PROJ # this.target = 0; //move to target using CAM # or PROJ #
this.dist = 0; this.dist = 0;
this.variables = {}; this.variables = {};
this.output = {}; this.output = {};
} }
/** /**
* Main function, accepts multi-line string, parses into lines * Main function, accepts multi-line string, parses into lines
* and interprets the instructions from the text. Returns an array * and interprets the instructions from the text. Returns an array
* of steps to be fed into the mcopy. * of steps to be fed into the mcopy.
*
* @param text {string} Mscript text to interpret
* @param callback {function} Function to call when string is interpreted
*
* returns {object} if callback is not provided
*/ */
interpret (text, callback) { interpret(text, callback) {
this.clear() this.clear();
if (typeof text === 'undefined') { if (typeof text === 'undefined') {
return this.fail('No input'); return this.fail('No input');
} }
//split string into lines, each containing a command //split string into lines, each containing a command
this.lines = text.split('\n'); this.lines = text.split('\n');
this.lines = this.lines.map(line => { this.lines = this.lines.map(line => {
line = line.replace(/\t+/g, ''); //strip tabs line = line.replace(/\t+/g, ''); //strip tabs
line = line.trim(); //remove excess whitespace before and after command line = line.trim(); //remove excess whitespace before and after command
line = line.toUpperCase(); line = line.toUpperCase();
return line; return line;
}) });
for (let line of this.lines) { for (let line of this.lines) {
this.two = line.substring(0, 2); this.two = line.substring(0, 2);
if (CMD.indexOf(this.two) !== -1) { if (CMD.indexOf(this.two) !== -1) {
this.basic_cmd(line); this.basic_cmd(line);
} else if (startsWith(line, '@') || line.indexOf('@') !== -1) { }
else if (startsWith(line, '@') || line.indexOf('@') !== -1) {
this.variable(line); this.variable(line);
} else if (startsWith(line, 'LOOP')) { }
else if (startsWith(line, 'LOOP')) {
this.new_loop(line); this.new_loop(line);
} else if (startsWith(line, 'L ')) { }
else if (startsWith(line, 'L ')) {
this.light_state(line); this.light_state(line);
} else if (startsWith(line, 'F ')) { }
else if (startsWith(line, 'F ')) {
this.new_loop(line, true); this.new_loop(line, true);
} else if (startsWith(line, 'END')) { }
else if (startsWith(line, 'END')) {
this.end_loop(line); this.end_loop(line);
} else if (startsWith(line, 'CAM')) { //directly go to that frame (black?) }
else if (startsWith(line, 'CAM')) { //directly go to that frame (black?)
this.move_cam(line); this.move_cam(line);
} else if (startsWith(line, 'PROJ')) { //directly go to that frame }
else if (startsWith(line, 'PROJ')) { //directly go to that frame
this.move_proj(line); this.move_proj(line);
} else if (startsWith(line, 'SET')) { //set that state }
else if (startsWith(line, 'SET')) { //set that state
this.set_state(line); this.set_state(line);
} else if (startsWith(line, '#') || startsWith(line, '//')) { }
else if (startsWith(line, '#') || startsWith(line, '//')) {
//comments //comments
//ignore while parsing //ignore while parsing
} }
} }
this.output.success = true; this.output.success = true;
this.output.arr = this.arr; //all instructions this.output.arr = this.arr; //all instructions
this.output.light = this.light; //all light instructions this.output.light = this.light; //all light instructions
this.output.cam = this.cam; this.output.cam = this.cam;
this.output.proj = this.proj; this.output.proj = this.proj;
if (typeof callback !== 'undefined') { if (typeof callback !== 'undefined') {
//should only be invoked by running mscript.tests() //should only be invoked by running mscript.tests()
callback(this.output); callback(this.output);
} else { }
else {
return this.output; return this.output;
} }
} }
variable (line) { variable(line) {
let parts = line.split('='); let parts = line.split('=');
let key = parts[0]; let key = parts[0];
let value = parts[1]; let value = parts[1];
let update = false; let update = false;
if (value && value.indexOf('#') !== -1) { if (value && value.indexOf('#') !== -1) {
value = value.split('#')[0]; value = value.split('#')[0];
} }
if (value && value.indexOf('//') !== -1) { if (value && value.indexOf('//') !== -1) {
value = value.split('//')[0]; value = value.split('//')[0];
} }
if (value && value.indexOf('+') !== -1) { if (value && value.indexOf('+') !== -1) {
if (value) if (value)
update = true; update = true;
} }
if (line.indexOf('-') !== -1) { if (line.indexOf('-') !== -1) {
update = true; update = true;
} }
if (line.indexOf(',') === -1) { //if not color string if (line.indexOf(',') === -1) { //if not color string
try { try {
value = parseInt(value); value = parseInt(value);
} catch (err) { }
catch (err) {
//supress parsing error //supress parsing error
} }
} }
@ -161,41 +162,42 @@ class Mscript {
if (!this.variables[key] || update) { if (!this.variables[key] || update) {
this.variables[key] = value; this.variables[key] = value;
} }
console.dir(this.variables) //console.dir(this.variables)
} }
variable_replace(line) { variable_replace(line) {
} }
/** /**
* Apply a basic two character command * Interpret a basic two character command
*
* @param line {string} Line of script to interpret
*/ */
basic_cmd (line) { basic_cmd(line) {
if (this.rec !== -1) { if (this.rec !== -1) {
//hold generated arr in state loop array //hold generated arr in state loop array
this.loops[this.rec].arr this.loops[this.rec].arr
.push.apply(this.loops[this.rec].arr, .push.apply(this.loops[this.rec].arr, this.str_to_arr(line, this.two));
this.str_to_arr(line,
this.two));
this.loops[this.rec].light this.loops[this.rec].light
.push.apply(this.loops[this.rec].light, .push.apply(this.loops[this.rec].light, this.light_to_arr(line, this.two));
this.light_to_arr(line, }
this.two)); else {
} else {
this.arr.push.apply(this.arr, this.str_to_arr(line, this.two)); this.arr.push.apply(this.arr, this.str_to_arr(line, this.two));
this.light.push.apply(this.light, this.light_to_arr(line, this.two)) this.light.push.apply(this.light, this.light_to_arr(line, this.two));
} }
} }
/** /**
* Start a new loop * Start a new loop
*
* @param line {string} Line to evaluate as either loop or fade
* @param fade {boolean} Flag as boolean if true
*/ */
new_loop (line, fade) { new_loop(line, fade) {
this.rec++; this.rec++;
this.loops[this.rec] = { this.loops[this.rec] = {
arr : [], arr: [],
light : [], light: [],
cam : 0, cam: 0,
proj : 0, proj: 0,
cmd : line + '' cmd: line + ''
}; };
if (fade) { if (fade) {
this.fade(line); this.fade(line);
@ -203,13 +205,14 @@ class Mscript {
} }
/** /**
* Close the most recent loop * Close the most recent loop
*
* @param line {string} Line to interpret
*/ */
end_loop (line) { end_loop(line) {
let light_arr; let light_arr;
let start; let start;
let end; let end;
let len; let len;
for (let x = 0; x < this.loop_count(this.loops[this.rec].cmd); x++) { for (let x = 0; x < this.loop_count(this.loops[this.rec].cmd); x++) {
light_arr = this.loops[this.rec].light; light_arr = this.loops[this.rec].light;
if (this.loops[this.rec].fade) { if (this.loops[this.rec].fade) {
@ -218,19 +221,17 @@ class Mscript {
len = this.loops[this.rec].fade_len; len = this.loops[this.rec].fade_len;
light_arr = light_arr.map(l => { light_arr = light_arr.map(l => {
return this.fade_rgb(start, end, len, x); return this.fade_rgb(start, end, len, x);
}) });
} }
if (this.rec === 0) { if (this.rec === 0) {
this.arr.push.apply(this.arr, this.loops[this.rec].arr); this.arr.push.apply(this.arr, this.loops[this.rec].arr);
this.light.push.apply(this.light, light_arr); this.light.push.apply(this.light, light_arr);
} else if (this.rec >= 1) { }
else if (this.rec >= 1) {
this.loops[this.rec - 1].arr this.loops[this.rec - 1].arr
.push.apply(this.loops[this.rec - 1].arr, .push.apply(this.loops[this.rec - 1].arr, this.loops[this.rec].arr);
this.loops[this.rec].arr);
this.loops[this.rec - 1].light this.loops[this.rec - 1].light
.push.apply(this.loops[this.rec - 1].light, .push.apply(this.loops[this.rec - 1].light, light_arr);
light_arr);
} }
} }
this.update('END', this.loop_count(this.loops[this.rec].cmd)); this.update('END', this.loop_count(this.loops[this.rec].cmd));
@ -239,8 +240,10 @@ class Mscript {
} }
/** /**
* Move camera to explicitly-defined frame * Move camera to explicitly-defined frame
*
* @param line {string} Line to interpret with camera move statement
*/ */
move_cam (line) { move_cam(line) {
this.target = parseInt(line.split('CAM ')[1]); this.target = parseInt(line.split('CAM ')[1]);
if (this.rec !== -1) { if (this.rec !== -1) {
if (this.target > this.cam) { if (this.target > this.cam) {
@ -250,7 +253,8 @@ class Mscript {
this.loops[this.rec].light.push(BLACK); this.loops[this.rec].light.push(BLACK);
this.update('BF'); this.update('BF');
} }
} else { }
else {
this.dist = this.cam - this.target; this.dist = this.cam - this.target;
for (let x = 0; x < this.dist; x++) { for (let x = 0; x < this.dist; x++) {
this.loops[this.rec].arr.push('BB'); this.loops[this.rec].arr.push('BB');
@ -258,7 +262,8 @@ class Mscript {
this.update('BB'); this.update('BB');
} }
} }
} else { }
else {
if (this.target > this.cam) { if (this.target > this.cam) {
this.dist = this.target - this.cam; this.dist = this.target - this.cam;
for (let x = 0; x < this.dist; x++) { for (let x = 0; x < this.dist; x++) {
@ -266,7 +271,8 @@ class Mscript {
this.light.push(BLACK); this.light.push(BLACK);
this.update('BF'); this.update('BF');
} }
} else { }
else {
this.dist = this.cam - this.target; this.dist = this.cam - this.target;
for (let x = 0; x < this.dist; x++) { for (let x = 0; x < this.dist; x++) {
this.arr.push('BB'); this.arr.push('BB');
@ -279,7 +285,7 @@ class Mscript {
/** /**
* Move projector to explicitly-defined frame * Move projector to explicitly-defined frame
*/ */
move_proj (line) { move_proj(line) {
this.target = parseInt(line.split('PROJ ')[1]); this.target = parseInt(line.split('PROJ ')[1]);
if (this.rec !== -1) { if (this.rec !== -1) {
if (this.target > this.proj) { if (this.target > this.proj) {
@ -289,7 +295,8 @@ class Mscript {
this.loops[this.rec].light.push(''); this.loops[this.rec].light.push('');
this.update('PF'); this.update('PF');
} }
} else { }
else {
this.dist = this.proj - this.target; this.dist = this.proj - this.target;
for (let x = 0; x < this.dist; x++) { for (let x = 0; x < this.dist; x++) {
this.loops[this.rec].arr.push('PB'); this.loops[this.rec].arr.push('PB');
@ -297,7 +304,8 @@ class Mscript {
this.update('PB'); this.update('PB');
} }
} }
} else { }
else {
if (this.target > this.proj) { if (this.target > this.proj) {
this.dist = this.target - this.proj; this.dist = this.target - this.proj;
for (let x = 0; x < this.dist; x++) { for (let x = 0; x < this.dist; x++) {
@ -305,7 +313,8 @@ class Mscript {
this.light.push(''); this.light.push('');
this.update('PF'); this.update('PF');
} }
} else { }
else {
this.dist = this.proj - this.target; this.dist = this.proj - this.target;
for (let x = 0; x < this.dist; x++) { for (let x = 0; x < this.dist; x++) {
this.arr.push('PB'); this.arr.push('PB');
@ -318,39 +327,39 @@ class Mscript {
/** /**
* Set the state of either the cam or projector * Set the state of either the cam or projector
*/ */
set_state (line) { set_state(line) {
if (startsWith(line, 'SET CAM')) { if (startsWith(line, 'SET CAM')) {
this.cam = parseInt(line.split('SET CAM')[1]); this.cam = parseInt(line.split('SET CAM')[1]);
} else if (startsWith(line, 'SET PROJ')) { }
else if (startsWith(line, 'SET PROJ')) {
this.proj = parseInt(line.split('SET PROJ')[1]); this.proj = parseInt(line.split('SET PROJ')[1]);
} }
} }
/** /**
* Return the last loop * Return the last loop
*/ */
last_loop () { last_loop() {
return this.loops[this.loops.length - 1]; return this.loops[this.loops.length - 1];
} }
/** /**
* Return the second-last loop * Return the second-last loop
*/ */
parent_loop () { parent_loop() {
return this.loops[this.loops.length - 2]; return this.loops[this.loops.length - 2];
} }
/** /**
* Extract the loop count integer from a LOOP cmd * Extract the loop count integer from a LOOP cmd
*/ */
loop_count (str) { loop_count(str) {
return parseInt(str.split(' ')[1]); return parseInt(str.split(' ')[1]);
} }
/** /**
* Execute a fade of frame length, from color to another color * Execute a fade of frame length, from color to another color
*/ */
fade (line) { fade(line) {
let len = this.fade_count(line); let len = this.fade_count(line);
let start = this.fade_start(line); let start = this.fade_start(line);
let end = this.fade_end(line); let end = this.fade_end(line);
this.loops[this.rec].start = start; this.loops[this.rec].start = start;
this.loops[this.rec].end = end; this.loops[this.rec].end = end;
this.loops[this.rec].fade = true; this.loops[this.rec].fade = true;
@ -360,115 +369,133 @@ class Mscript {
/** /**
* Extract the fade length integer from a FADE cmd * Extract the fade length integer from a FADE cmd
*/ */
fade_count (str) { fade_count(str) {
return parseInt(str.split(' ')[1]); return parseInt(str.split(' ')[1]);
} }
/** /**
* Extract the start color from a string * Extract the start color from a string
*/ */
fade_start (str) { fade_start(str) {
let color = str.split(' ')[2]; let color = str.split(' ')[2];
return this.rgb(color.trim()) return this.rgb(color.trim());
} }
/** /**
* Extract the end color from a string * Extract the end color from a string
*/ */
fade_end (str) { fade_end(str) {
let color = str.split(' ')[3]; let color = str.split(' ')[3];
return this.rgb(color.trim()) return this.rgb(color.trim());
} }
fade_rgb (start, end, len, x) { fade_rgb(start, end, len, x) {
let cur = []; let cur = [];
let diff; let diff;
for (let i = 0; i < 3; i++) { for (let i = 0; i < 3; i++) {
if (x === len - 1) { if (x === len - 1) {
cur[i] = end[i]; cur[i] = end[i];
} else if (start[i] >= end[i]) { }
else if (start[i] >= end[i]) {
diff = start[i] - end[i]; diff = start[i] - end[i];
cur[i] = start[i] - Math.round((diff / (len - 1)) * x); cur[i] = start[i] - Math.round((diff / (len - 1)) * x);
} else { }
else {
diff = end[i] - start[i]; diff = end[i] - start[i];
cur[i] = start[i] + Math.round((diff / (len - 1)) * x); cur[i] = start[i] + Math.round((diff / (len - 1)) * x);
} }
} }
return this.rgb_str(cur); return this.rgb_str(cur);
} }
rgb (str) { rgb(str) {
let rgb = str.split(','); let rgb = str.split(',');
return rgb.map( char => { return rgb.map((char) => {
return parseInt(char); return parseInt(char);
}) });
} }
rgb_str (arr) { /**
*
**/
rgb_str(arr) {
return arr.join(','); return arr.join(',');
} }
/** /**
* Increase the state of a specific object, such as the camera/projector, * Increase the state of a specific object, such as the camera/projector,
* by the value defined in val * by the value defined in val
*/ */
update (cmd, val = 1) { update(cmd, val = 1) {
if (cmd === 'END') { if (cmd === 'END') {
//I don't understand this loop //I don't understand this loop
for (let i = 0; i < val; i++) { for (let i = 0; i < val; i++) {
if (this.rec === 0) { if (this.rec === 0) {
this.cam += this.loops[this.rec].cam; this.cam += this.loops[this.rec].cam;
this.proj += this.loops[this.rec].proj; this.proj += this.loops[this.rec].proj;
} else if (this.rec >= 1) { }
else if (this.rec >= 1) {
this.loops[this.rec - 1].cam += this.loops[this.rec].cam; this.loops[this.rec - 1].cam += this.loops[this.rec].cam;
this.loops[this.rec - 1].proj += this.loops[this.rec].proj; this.loops[this.rec - 1].proj += this.loops[this.rec].proj;
} }
} }
} else if (cmd === 'CF') { }
else if (cmd === 'CF') {
if (this.rec === -1) { if (this.rec === -1) {
this.cam += val; this.cam += val;
} else { }
else {
this.loops[this.rec].cam += val; this.loops[this.rec].cam += val;
} }
} else if (cmd === 'CB') { }
else if (cmd === 'CB') {
if (this.rec === -1) { if (this.rec === -1) {
this.cam -= val; this.cam -= val;
} else { }
else {
this.loops[this.rec].cam--; this.loops[this.rec].cam--;
} }
} else if (cmd === 'PF') { }
else if (cmd === 'PF') {
if (this.rec === -1) { if (this.rec === -1) {
this.proj += val; this.proj += val;
} else { }
else {
this.loops[this.rec].proj += val; this.loops[this.rec].proj += val;
} }
} else if (cmd === 'PB') { }
else if (cmd === 'PB') {
if (this.rec === -1) { if (this.rec === -1) {
this.proj -= val; this.proj -= val;
} else { }
else {
this.loops[this.rec].proj--; this.loops[this.rec].proj--;
} }
} else if (cmd === 'BF') { }
else if (cmd === 'BF') {
if (this.rec === -1) { if (this.rec === -1) {
this.cam += val; this.cam += val;
} else { }
else {
this.loops[this.rec].cam += val; this.loops[this.rec].cam += val;
} }
} else if (cmd === 'BB') { }
else if (cmd === 'BB') {
if (this.rec === -1) { if (this.rec === -1) {
this.cam -= val; this.cam -= val;
} else { }
else {
this.loops[this.rec].cam -= val; this.loops[this.rec].cam -= val;
} }
} else if (cmd === 'L ') { }
else if (cmd === 'L ') {
} }
} }
/** /**
* Split string on command, extract any integers from string * Split string on command, extract any integers from string
*/ */
str_to_arr (str, cmd) { str_to_arr(str, cmd) {
const cnt = str.split(cmd); const cnt = str.split(cmd);
let c = parseInt(cnt[1]); let c = parseInt(cnt[1]);
let arr = []; let arr = [];
if (cnt[1] === '') { if (cnt[1] === '') {
c = 1; c = 1;
} else { }
else {
c = parseInt(cnt[1]); c = parseInt(cnt[1]);
} }
arr = new Array(c).fill(cmd); arr = new Array(c).fill(cmd);
@ -478,23 +505,26 @@ class Mscript {
/** /**
* Split a string on a command to extract data for light array * Split a string on a command to extract data for light array
*/ */
light_to_arr (str, cmd) { light_to_arr(str, cmd) {
const cnt = str.split(cmd); const cnt = str.split(cmd);
let c = parseInt(cnt[1]); let c = parseInt(cnt[1]);
let arr = []; let arr = [];
if (cnt[1] === '') { if (cnt[1] === '') {
c = 1; c = 1;
} else { }
else {
c = parseInt(cnt[1]); c = parseInt(cnt[1]);
} }
for (var i = 0; i < c; i++) { for (let i = 0; i < c; i++) {
if (cmd === 'CF' if (cmd === 'CF'
|| cmd === 'CB') { || cmd === 'CB') {
arr.push(this.color); arr.push(this.color);
} else if (cmd === 'BF' }
else if (cmd === 'BF'
|| cmd === 'BB') { || cmd === 'BB') {
arr.push(BLACK); arr.push(BLACK);
} else { }
else {
arr.push(''); arr.push('');
} }
} }
@ -503,43 +533,19 @@ class Mscript {
/** /**
* Split a string to extract an rgb color value * Split a string to extract an rgb color value
*/ */
light_state (str) { light_state(str) {
//add parsers for other color spaces //add parsers for other color spaces
const color = str.replace('L ', '').trim(); const color = str.replace('L ', '').trim();
this.color = color; this.color = color;
} }
/** /**
* Throw an error with specific message * Throw an error with specific message
*
* @param msg {string} Error message to print
*/ */
fail (msg) { fail(msg) {
throw new Error(msg); throw new Error(msg);
} }
} }
module.exports = Mscript; module.exports = Mscript;
//# sourceMappingURL=index.js.map
/*
CAM # - go to camera frame #
PROJ # - go to projector frame #
SET CAM # - sets camera count to #
SET PROJ # - sets projector 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
CF - Camera forwards
PF - Projector forwards
BF - Black forwards
CB - Camera backwards
PB - Projector backwards
BB - Black backwards
*/

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,4 @@
'use strict'; 'use strict';
/** @module lib/mscript */
const BLACK = '0,0,0'; const BLACK = '0,0,0';
const WHITE = '255,255,255'; const WHITE = '255,255,255';
const CMD = [ const CMD = [
@ -13,147 +10,151 @@ const CMD = [
'BB' 'BB'
]; ];
const ALTS = { const ALTS = {
'CF' : ['CAMERA FORWARD', 'CAM FORWARD'], 'CF': ['CAMERA FORWARD', 'CAM FORWARD'],
'PF' : ['PROJECTOR FORWARD', 'PROJ FORWARD'], 'PF': ['PROJECTOR FORWARD', 'PROJ FORWARD'],
'BF' : ['BLACK FORWARD', 'BLACK', 'BLANK FORWARD', 'BLANK'], 'BF': ['BLACK FORWARD', 'BLACK', 'BLANK FORWARD', 'BLANK'],
'CB' : ['CAMERA BACKWARD', 'CAM BACKWARD', 'CAMERA BACK', 'CAM BACK'], 'CB': ['CAMERA BACKWARD', 'CAM BACKWARD', 'CAMERA BACK', 'CAM BACK'],
'PB' : ['PROJECTOR FORWARD', 'PROJ FORWARD', 'PROJECTOR BACK', 'PROJ BACK'], 'PB': ['PROJECTOR FORWARD', 'PROJ FORWARD', 'PROJECTOR BACK', 'PROJ BACK'],
'BB' : ['BLACK BACKWARD', 'BLACK BACK', 'BLANK BACK'], 'BB': ['BLACK BACKWARD', 'BLACK BACK', 'BLANK BACK'],
'L ' : ['LIGHT', 'COLOR', 'LAMP'], 'L ': ['LIGHT', 'COLOR', 'LAMP'],
'F ' : ['FADE'] 'F ': ['FADE']
}; };
/** helper functions */ /** helper functions */
/** startswith function from lodash, do not want the entire lib for this
/** startswith function from lodash, do not want the entire lib for this */ * @param str {string} Text to evaluate
function startsWith(string, target, position) { * @param target {string} Text to compare string against
const { length } = string; * @param position {integer} Position in the string to make comparison at
*
* @returns {boolean} True for match, false for no match
**/
function startsWith(str, target, position) {
const { length } = str;
position = position == null ? 0 : position; position = position == null ? 0 : position;
if (position < 0) { if (position < 0) {
position = 0; position = 0;
} else if (position > length) { }
else if (position > length) {
position = length; position = length;
} }
target = `${target}`; target = `${target}`;
return string.slice(position, position + target.length) == target; return str.slice(position, position + target.length) == target;
} }
/** class Mscript */ /** class Mscript */
class Mscript { class Mscript {
constructor () { constructor() {
this.output = {}; this.output = {};
} }
/** /**
* Clear the state of the script * Clear the state of the script
*/ */
clear () { clear() {
this.lines = []; this.lines = [];
this.cam = 0; this.cam = 0;
this.proj = 0; this.proj = 0;
this.color = ''; this.color = '';
this.loops = []; this.loops = [];
this.rec = -1; this.rec = -1;
this.two = ''; this.two = '';
this.arr = []; this.arr = [];
this.light = []; this.light = [];
this.target = 0; //move to target using CAM # or PROJ # this.target = 0; //move to target using CAM # or PROJ #
this.dist = 0; this.dist = 0;
this.variables = {}; this.variables = {};
this.output = {}; this.output = {};
} }
/** /**
* Main function, accepts multi-line string, parses into lines * Main function, accepts multi-line string, parses into lines
* and interprets the instructions from the text. Returns an array * and interprets the instructions from the text. Returns an array
* of steps to be fed into the mcopy. * of steps to be fed into the mcopy.
*
* @param text {string} Mscript text to interpret
* @param callback {function} Function to call when string is interpreted
*
* returns {object} if callback is not provided
*/ */
interpret (text, callback) { interpret(text, callback) {
this.clear() this.clear();
if (typeof text === 'undefined') { if (typeof text === 'undefined') {
return this.fail('No input'); return this.fail('No input');
} }
//split string into lines, each containing a command //split string into lines, each containing a command
this.lines = text.split('\n'); this.lines = text.split('\n');
this.lines = this.lines.map(line => { this.lines = this.lines.map(line => {
line = line.replace(/\t+/g, ''); //strip tabs line = line.replace(/\t+/g, ''); //strip tabs
line = line.trim(); //remove excess whitespace before and after command line = line.trim(); //remove excess whitespace before and after command
line = line.toUpperCase(); line = line.toUpperCase();
return line; return line;
}) });
for (let line of this.lines) { for (let line of this.lines) {
this.two = line.substring(0, 2); this.two = line.substring(0, 2);
if (CMD.indexOf(this.two) !== -1) { if (CMD.indexOf(this.two) !== -1) {
this.basic_cmd(line); this.basic_cmd(line);
} else if (startsWith(line, '@') || line.indexOf('@') !== -1) { }
else if (startsWith(line, '@') || line.indexOf('@') !== -1) {
this.variable(line); this.variable(line);
} else if (startsWith(line, 'LOOP')) { }
else if (startsWith(line, 'LOOP')) {
this.new_loop(line); this.new_loop(line);
} else if (startsWith(line, 'L ')) { }
else if (startsWith(line, 'L ')) {
this.light_state(line); this.light_state(line);
} else if (startsWith(line, 'F ')) { }
else if (startsWith(line, 'F ')) {
this.new_loop(line, true); this.new_loop(line, true);
} else if (startsWith(line, 'END')) { }
else if (startsWith(line, 'END')) {
this.end_loop(line); this.end_loop(line);
} else if (startsWith(line, 'CAM')) { //directly go to that frame (black?) }
else if (startsWith(line, 'CAM')) { //directly go to that frame (black?)
this.move_cam(line); this.move_cam(line);
} else if (startsWith(line, 'PROJ')) { //directly go to that frame }
else if (startsWith(line, 'PROJ')) { //directly go to that frame
this.move_proj(line); this.move_proj(line);
} else if (startsWith(line, 'SET')) { //set that state }
else if (startsWith(line, 'SET')) { //set that state
this.set_state(line); this.set_state(line);
} else if (startsWith(line, '#') || startsWith(line, '//')) { }
else if (startsWith(line, '#') || startsWith(line, '//')) {
//comments //comments
//ignore while parsing //ignore while parsing
} }
} }
this.output.success = true; this.output.success = true;
this.output.arr = this.arr; //all instructions this.output.arr = this.arr; //all instructions
this.output.light = this.light; //all light instructions this.output.light = this.light; //all light instructions
this.output.cam = this.cam; this.output.cam = this.cam;
this.output.proj = this.proj; this.output.proj = this.proj;
if (typeof callback !== 'undefined') { if (typeof callback !== 'undefined') {
//should only be invoked by running mscript.tests() //should only be invoked by running mscript.tests()
callback(this.output); callback(this.output);
} else { }
else {
return this.output; return this.output;
} }
} }
variable (line) { variable(line) {
let parts = line.split('='); let parts = line.split('=');
let key = parts[0]; let key = parts[0];
let value = parts[1]; let value = parts[1];
let update = false; let update = false;
if (value && value.indexOf('#') !== -1) { if (value && value.indexOf('#') !== -1) {
value = value.split('#')[0]; value = value.split('#')[0];
} }
if (value && value.indexOf('//') !== -1) { if (value && value.indexOf('//') !== -1) {
value = value.split('//')[0]; value = value.split('//')[0];
} }
if (value && value.indexOf('+') !== -1) { if (value && value.indexOf('+') !== -1) {
if (value) if (value)
update = true; update = true;
} }
if (line.indexOf('-') !== -1) { if (line.indexOf('-') !== -1) {
update = true; update = true;
} }
if (line.indexOf(',') === -1) { //if not color string if (line.indexOf(',') === -1) { //if not color string
try { try {
value = parseInt(value); value = parseInt(value);
} catch (err) { }
catch (err) {
//supress parsing error //supress parsing error
} }
} }
@ -161,41 +162,42 @@ class Mscript {
if (!this.variables[key] || update) { if (!this.variables[key] || update) {
this.variables[key] = value; this.variables[key] = value;
} }
console.dir(this.variables) //console.dir(this.variables)
} }
variable_replace(line) { variable_replace(line) {
} }
/** /**
* Apply a basic two character command * Interpret a basic two character command
*
* @param line {string} Line of script to interpret
*/ */
basic_cmd (line) { basic_cmd(line) {
if (this.rec !== -1) { if (this.rec !== -1) {
//hold generated arr in state loop array //hold generated arr in state loop array
this.loops[this.rec].arr this.loops[this.rec].arr
.push.apply(this.loops[this.rec].arr, .push.apply(this.loops[this.rec].arr, this.str_to_arr(line, this.two));
this.str_to_arr(line,
this.two));
this.loops[this.rec].light this.loops[this.rec].light
.push.apply(this.loops[this.rec].light, .push.apply(this.loops[this.rec].light, this.light_to_arr(line, this.two));
this.light_to_arr(line, }
this.two)); else {
} else {
this.arr.push.apply(this.arr, this.str_to_arr(line, this.two)); this.arr.push.apply(this.arr, this.str_to_arr(line, this.two));
this.light.push.apply(this.light, this.light_to_arr(line, this.two)) this.light.push.apply(this.light, this.light_to_arr(line, this.two));
} }
} }
/** /**
* Start a new loop * Start a new loop
*
* @param line {string} Line to evaluate as either loop or fade
* @param fade {boolean} Flag as boolean if true
*/ */
new_loop (line, fade) { new_loop(line, fade) {
this.rec++; this.rec++;
this.loops[this.rec] = { this.loops[this.rec] = {
arr : [], arr: [],
light : [], light: [],
cam : 0, cam: 0,
proj : 0, proj: 0,
cmd : line + '' cmd: line + ''
}; };
if (fade) { if (fade) {
this.fade(line); this.fade(line);
@ -203,13 +205,14 @@ class Mscript {
} }
/** /**
* Close the most recent loop * Close the most recent loop
*
* @param line {string} Line to interpret
*/ */
end_loop (line) { end_loop(line) {
let light_arr; let light_arr;
let start; let start;
let end; let end;
let len; let len;
for (let x = 0; x < this.loop_count(this.loops[this.rec].cmd); x++) { for (let x = 0; x < this.loop_count(this.loops[this.rec].cmd); x++) {
light_arr = this.loops[this.rec].light; light_arr = this.loops[this.rec].light;
if (this.loops[this.rec].fade) { if (this.loops[this.rec].fade) {
@ -218,19 +221,17 @@ class Mscript {
len = this.loops[this.rec].fade_len; len = this.loops[this.rec].fade_len;
light_arr = light_arr.map(l => { light_arr = light_arr.map(l => {
return this.fade_rgb(start, end, len, x); return this.fade_rgb(start, end, len, x);
}) });
} }
if (this.rec === 0) { if (this.rec === 0) {
this.arr.push.apply(this.arr, this.loops[this.rec].arr); this.arr.push.apply(this.arr, this.loops[this.rec].arr);
this.light.push.apply(this.light, light_arr); this.light.push.apply(this.light, light_arr);
} else if (this.rec >= 1) { }
else if (this.rec >= 1) {
this.loops[this.rec - 1].arr this.loops[this.rec - 1].arr
.push.apply(this.loops[this.rec - 1].arr, .push.apply(this.loops[this.rec - 1].arr, this.loops[this.rec].arr);
this.loops[this.rec].arr);
this.loops[this.rec - 1].light this.loops[this.rec - 1].light
.push.apply(this.loops[this.rec - 1].light, .push.apply(this.loops[this.rec - 1].light, light_arr);
light_arr);
} }
} }
this.update('END', this.loop_count(this.loops[this.rec].cmd)); this.update('END', this.loop_count(this.loops[this.rec].cmd));
@ -239,8 +240,10 @@ class Mscript {
} }
/** /**
* Move camera to explicitly-defined frame * Move camera to explicitly-defined frame
*
* @param line {string} Line to interpret with camera move statement
*/ */
move_cam (line) { move_cam(line) {
this.target = parseInt(line.split('CAM ')[1]); this.target = parseInt(line.split('CAM ')[1]);
if (this.rec !== -1) { if (this.rec !== -1) {
if (this.target > this.cam) { if (this.target > this.cam) {
@ -250,7 +253,8 @@ class Mscript {
this.loops[this.rec].light.push(BLACK); this.loops[this.rec].light.push(BLACK);
this.update('BF'); this.update('BF');
} }
} else { }
else {
this.dist = this.cam - this.target; this.dist = this.cam - this.target;
for (let x = 0; x < this.dist; x++) { for (let x = 0; x < this.dist; x++) {
this.loops[this.rec].arr.push('BB'); this.loops[this.rec].arr.push('BB');
@ -258,7 +262,8 @@ class Mscript {
this.update('BB'); this.update('BB');
} }
} }
} else { }
else {
if (this.target > this.cam) { if (this.target > this.cam) {
this.dist = this.target - this.cam; this.dist = this.target - this.cam;
for (let x = 0; x < this.dist; x++) { for (let x = 0; x < this.dist; x++) {
@ -266,7 +271,8 @@ class Mscript {
this.light.push(BLACK); this.light.push(BLACK);
this.update('BF'); this.update('BF');
} }
} else { }
else {
this.dist = this.cam - this.target; this.dist = this.cam - this.target;
for (let x = 0; x < this.dist; x++) { for (let x = 0; x < this.dist; x++) {
this.arr.push('BB'); this.arr.push('BB');
@ -279,7 +285,7 @@ class Mscript {
/** /**
* Move projector to explicitly-defined frame * Move projector to explicitly-defined frame
*/ */
move_proj (line) { move_proj(line) {
this.target = parseInt(line.split('PROJ ')[1]); this.target = parseInt(line.split('PROJ ')[1]);
if (this.rec !== -1) { if (this.rec !== -1) {
if (this.target > this.proj) { if (this.target > this.proj) {
@ -289,7 +295,8 @@ class Mscript {
this.loops[this.rec].light.push(''); this.loops[this.rec].light.push('');
this.update('PF'); this.update('PF');
} }
} else { }
else {
this.dist = this.proj - this.target; this.dist = this.proj - this.target;
for (let x = 0; x < this.dist; x++) { for (let x = 0; x < this.dist; x++) {
this.loops[this.rec].arr.push('PB'); this.loops[this.rec].arr.push('PB');
@ -297,7 +304,8 @@ class Mscript {
this.update('PB'); this.update('PB');
} }
} }
} else { }
else {
if (this.target > this.proj) { if (this.target > this.proj) {
this.dist = this.target - this.proj; this.dist = this.target - this.proj;
for (let x = 0; x < this.dist; x++) { for (let x = 0; x < this.dist; x++) {
@ -305,7 +313,8 @@ class Mscript {
this.light.push(''); this.light.push('');
this.update('PF'); this.update('PF');
} }
} else { }
else {
this.dist = this.proj - this.target; this.dist = this.proj - this.target;
for (let x = 0; x < this.dist; x++) { for (let x = 0; x < this.dist; x++) {
this.arr.push('PB'); this.arr.push('PB');
@ -318,39 +327,39 @@ class Mscript {
/** /**
* Set the state of either the cam or projector * Set the state of either the cam or projector
*/ */
set_state (line) { set_state(line) {
if (startsWith(line, 'SET CAM')) { if (startsWith(line, 'SET CAM')) {
this.cam = parseInt(line.split('SET CAM')[1]); this.cam = parseInt(line.split('SET CAM')[1]);
} else if (startsWith(line, 'SET PROJ')) { }
else if (startsWith(line, 'SET PROJ')) {
this.proj = parseInt(line.split('SET PROJ')[1]); this.proj = parseInt(line.split('SET PROJ')[1]);
} }
} }
/** /**
* Return the last loop * Return the last loop
*/ */
last_loop () { last_loop() {
return this.loops[this.loops.length - 1]; return this.loops[this.loops.length - 1];
} }
/** /**
* Return the second-last loop * Return the second-last loop
*/ */
parent_loop () { parent_loop() {
return this.loops[this.loops.length - 2]; return this.loops[this.loops.length - 2];
} }
/** /**
* Extract the loop count integer from a LOOP cmd * Extract the loop count integer from a LOOP cmd
*/ */
loop_count (str) { loop_count(str) {
return parseInt(str.split(' ')[1]); return parseInt(str.split(' ')[1]);
} }
/** /**
* Execute a fade of frame length, from color to another color * Execute a fade of frame length, from color to another color
*/ */
fade (line) { fade(line) {
let len = this.fade_count(line); let len = this.fade_count(line);
let start = this.fade_start(line); let start = this.fade_start(line);
let end = this.fade_end(line); let end = this.fade_end(line);
this.loops[this.rec].start = start; this.loops[this.rec].start = start;
this.loops[this.rec].end = end; this.loops[this.rec].end = end;
this.loops[this.rec].fade = true; this.loops[this.rec].fade = true;
@ -360,115 +369,133 @@ class Mscript {
/** /**
* Extract the fade length integer from a FADE cmd * Extract the fade length integer from a FADE cmd
*/ */
fade_count (str) { fade_count(str) {
return parseInt(str.split(' ')[1]); return parseInt(str.split(' ')[1]);
} }
/** /**
* Extract the start color from a string * Extract the start color from a string
*/ */
fade_start (str) { fade_start(str) {
let color = str.split(' ')[2]; let color = str.split(' ')[2];
return this.rgb(color.trim()) return this.rgb(color.trim());
} }
/** /**
* Extract the end color from a string * Extract the end color from a string
*/ */
fade_end (str) { fade_end(str) {
let color = str.split(' ')[3]; let color = str.split(' ')[3];
return this.rgb(color.trim()) return this.rgb(color.trim());
} }
fade_rgb (start, end, len, x) { fade_rgb(start, end, len, x) {
let cur = []; let cur = [];
let diff; let diff;
for (let i = 0; i < 3; i++) { for (let i = 0; i < 3; i++) {
if (x === len - 1) { if (x === len - 1) {
cur[i] = end[i]; cur[i] = end[i];
} else if (start[i] >= end[i]) { }
else if (start[i] >= end[i]) {
diff = start[i] - end[i]; diff = start[i] - end[i];
cur[i] = start[i] - Math.round((diff / (len - 1)) * x); cur[i] = start[i] - Math.round((diff / (len - 1)) * x);
} else { }
else {
diff = end[i] - start[i]; diff = end[i] - start[i];
cur[i] = start[i] + Math.round((diff / (len - 1)) * x); cur[i] = start[i] + Math.round((diff / (len - 1)) * x);
} }
} }
return this.rgb_str(cur); return this.rgb_str(cur);
} }
rgb (str) { rgb(str) {
let rgb = str.split(','); let rgb = str.split(',');
return rgb.map( char => { return rgb.map((char) => {
return parseInt(char); return parseInt(char);
}) });
} }
rgb_str (arr) { /**
*
**/
rgb_str(arr) {
return arr.join(','); return arr.join(',');
} }
/** /**
* Increase the state of a specific object, such as the camera/projector, * Increase the state of a specific object, such as the camera/projector,
* by the value defined in val * by the value defined in val
*/ */
update (cmd, val = 1) { update(cmd, val = 1) {
if (cmd === 'END') { if (cmd === 'END') {
//I don't understand this loop //I don't understand this loop
for (let i = 0; i < val; i++) { for (let i = 0; i < val; i++) {
if (this.rec === 0) { if (this.rec === 0) {
this.cam += this.loops[this.rec].cam; this.cam += this.loops[this.rec].cam;
this.proj += this.loops[this.rec].proj; this.proj += this.loops[this.rec].proj;
} else if (this.rec >= 1) { }
else if (this.rec >= 1) {
this.loops[this.rec - 1].cam += this.loops[this.rec].cam; this.loops[this.rec - 1].cam += this.loops[this.rec].cam;
this.loops[this.rec - 1].proj += this.loops[this.rec].proj; this.loops[this.rec - 1].proj += this.loops[this.rec].proj;
} }
} }
} else if (cmd === 'CF') { }
else if (cmd === 'CF') {
if (this.rec === -1) { if (this.rec === -1) {
this.cam += val; this.cam += val;
} else { }
else {
this.loops[this.rec].cam += val; this.loops[this.rec].cam += val;
} }
} else if (cmd === 'CB') { }
else if (cmd === 'CB') {
if (this.rec === -1) { if (this.rec === -1) {
this.cam -= val; this.cam -= val;
} else { }
else {
this.loops[this.rec].cam--; this.loops[this.rec].cam--;
} }
} else if (cmd === 'PF') { }
else if (cmd === 'PF') {
if (this.rec === -1) { if (this.rec === -1) {
this.proj += val; this.proj += val;
} else { }
else {
this.loops[this.rec].proj += val; this.loops[this.rec].proj += val;
} }
} else if (cmd === 'PB') { }
else if (cmd === 'PB') {
if (this.rec === -1) { if (this.rec === -1) {
this.proj -= val; this.proj -= val;
} else { }
else {
this.loops[this.rec].proj--; this.loops[this.rec].proj--;
} }
} else if (cmd === 'BF') { }
else if (cmd === 'BF') {
if (this.rec === -1) { if (this.rec === -1) {
this.cam += val; this.cam += val;
} else { }
else {
this.loops[this.rec].cam += val; this.loops[this.rec].cam += val;
} }
} else if (cmd === 'BB') { }
else if (cmd === 'BB') {
if (this.rec === -1) { if (this.rec === -1) {
this.cam -= val; this.cam -= val;
} else { }
else {
this.loops[this.rec].cam -= val; this.loops[this.rec].cam -= val;
} }
} else if (cmd === 'L ') { }
else if (cmd === 'L ') {
} }
} }
/** /**
* Split string on command, extract any integers from string * Split string on command, extract any integers from string
*/ */
str_to_arr (str, cmd) { str_to_arr(str, cmd) {
const cnt = str.split(cmd); const cnt = str.split(cmd);
let c = parseInt(cnt[1]); let c = parseInt(cnt[1]);
let arr = []; let arr = [];
if (cnt[1] === '') { if (cnt[1] === '') {
c = 1; c = 1;
} else { }
else {
c = parseInt(cnt[1]); c = parseInt(cnt[1]);
} }
arr = new Array(c).fill(cmd); arr = new Array(c).fill(cmd);
@ -478,23 +505,26 @@ class Mscript {
/** /**
* Split a string on a command to extract data for light array * Split a string on a command to extract data for light array
*/ */
light_to_arr (str, cmd) { light_to_arr(str, cmd) {
const cnt = str.split(cmd); const cnt = str.split(cmd);
let c = parseInt(cnt[1]); let c = parseInt(cnt[1]);
let arr = []; let arr = [];
if (cnt[1] === '') { if (cnt[1] === '') {
c = 1; c = 1;
} else { }
else {
c = parseInt(cnt[1]); c = parseInt(cnt[1]);
} }
for (var i = 0; i < c; i++) { for (let i = 0; i < c; i++) {
if (cmd === 'CF' if (cmd === 'CF'
|| cmd === 'CB') { || cmd === 'CB') {
arr.push(this.color); arr.push(this.color);
} else if (cmd === 'BF' }
else if (cmd === 'BF'
|| cmd === 'BB') { || cmd === 'BB') {
arr.push(BLACK); arr.push(BLACK);
} else { }
else {
arr.push(''); arr.push('');
} }
} }
@ -503,43 +533,19 @@ class Mscript {
/** /**
* Split a string to extract an rgb color value * Split a string to extract an rgb color value
*/ */
light_state (str) { light_state(str) {
//add parsers for other color spaces //add parsers for other color spaces
const color = str.replace('L ', '').trim(); const color = str.replace('L ', '').trim();
this.color = color; this.color = color;
} }
/** /**
* Throw an error with specific message * Throw an error with specific message
*
* @param msg {string} Error message to print
*/ */
fail (msg) { fail(msg) {
throw new Error(msg); throw new Error(msg);
} }
} }
module.exports = Mscript; module.exports = Mscript;
//# sourceMappingURL=index.js.map
/*
CAM # - go to camera frame #
PROJ # - go to projector frame #
SET CAM # - sets camera count to #
SET PROJ # - sets projector 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
CF - Camera forwards
PF - Projector forwards
BF - Black forwards
CB - Camera backwards
PB - Projector backwards
BB - Black backwards
*/

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,4 @@
'use strict'; 'use strict';
/** @module lib/mscript */
const BLACK = '0,0,0'; const BLACK = '0,0,0';
const WHITE = '255,255,255'; const WHITE = '255,255,255';
const CMD = [ const CMD = [
@ -13,147 +10,151 @@ const CMD = [
'BB' 'BB'
]; ];
const ALTS = { const ALTS = {
'CF' : ['CAMERA FORWARD', 'CAM FORWARD'], 'CF': ['CAMERA FORWARD', 'CAM FORWARD'],
'PF' : ['PROJECTOR FORWARD', 'PROJ FORWARD'], 'PF': ['PROJECTOR FORWARD', 'PROJ FORWARD'],
'BF' : ['BLACK FORWARD', 'BLACK', 'BLANK FORWARD', 'BLANK'], 'BF': ['BLACK FORWARD', 'BLACK', 'BLANK FORWARD', 'BLANK'],
'CB' : ['CAMERA BACKWARD', 'CAM BACKWARD', 'CAMERA BACK', 'CAM BACK'], 'CB': ['CAMERA BACKWARD', 'CAM BACKWARD', 'CAMERA BACK', 'CAM BACK'],
'PB' : ['PROJECTOR FORWARD', 'PROJ FORWARD', 'PROJECTOR BACK', 'PROJ BACK'], 'PB': ['PROJECTOR FORWARD', 'PROJ FORWARD', 'PROJECTOR BACK', 'PROJ BACK'],
'BB' : ['BLACK BACKWARD', 'BLACK BACK', 'BLANK BACK'], 'BB': ['BLACK BACKWARD', 'BLACK BACK', 'BLANK BACK'],
'L ' : ['LIGHT', 'COLOR', 'LAMP'], 'L ': ['LIGHT', 'COLOR', 'LAMP'],
'F ' : ['FADE'] 'F ': ['FADE']
}; };
/** helper functions */ /** helper functions */
/** startswith function from lodash, do not want the entire lib for this
/** startswith function from lodash, do not want the entire lib for this */ * @param str {string} Text to evaluate
function startsWith(string, target, position) { * @param target {string} Text to compare string against
const { length } = string; * @param position {integer} Position in the string to make comparison at
*
* @returns {boolean} True for match, false for no match
**/
function startsWith(str, target, position) {
const { length } = str;
position = position == null ? 0 : position; position = position == null ? 0 : position;
if (position < 0) { if (position < 0) {
position = 0; position = 0;
} else if (position > length) { }
else if (position > length) {
position = length; position = length;
} }
target = `${target}`; target = `${target}`;
return string.slice(position, position + target.length) == target; return str.slice(position, position + target.length) == target;
} }
/** class Mscript */ /** class Mscript */
class Mscript { class Mscript {
constructor () { constructor() {
this.output = {}; this.output = {};
} }
/** /**
* Clear the state of the script * Clear the state of the script
*/ */
clear () { clear() {
this.lines = []; this.lines = [];
this.cam = 0; this.cam = 0;
this.proj = 0; this.proj = 0;
this.color = ''; this.color = '';
this.loops = []; this.loops = [];
this.rec = -1; this.rec = -1;
this.two = ''; this.two = '';
this.arr = []; this.arr = [];
this.light = []; this.light = [];
this.target = 0; //move to target using CAM # or PROJ # this.target = 0; //move to target using CAM # or PROJ #
this.dist = 0; this.dist = 0;
this.variables = {}; this.variables = {};
this.output = {}; this.output = {};
} }
/** /**
* Main function, accepts multi-line string, parses into lines * Main function, accepts multi-line string, parses into lines
* and interprets the instructions from the text. Returns an array * and interprets the instructions from the text. Returns an array
* of steps to be fed into the mcopy. * of steps to be fed into the mcopy.
*
* @param text {string} Mscript text to interpret
* @param callback {function} Function to call when string is interpreted
*
* returns {object} if callback is not provided
*/ */
interpret (text, callback) { interpret(text, callback) {
this.clear() this.clear();
if (typeof text === 'undefined') { if (typeof text === 'undefined') {
return this.fail('No input'); return this.fail('No input');
} }
//split string into lines, each containing a command //split string into lines, each containing a command
this.lines = text.split('\n'); this.lines = text.split('\n');
this.lines = this.lines.map(line => { this.lines = this.lines.map(line => {
line = line.replace(/\t+/g, ''); //strip tabs line = line.replace(/\t+/g, ''); //strip tabs
line = line.trim(); //remove excess whitespace before and after command line = line.trim(); //remove excess whitespace before and after command
line = line.toUpperCase(); line = line.toUpperCase();
return line; return line;
}) });
for (let line of this.lines) { for (let line of this.lines) {
this.two = line.substring(0, 2); this.two = line.substring(0, 2);
if (CMD.indexOf(this.two) !== -1) { if (CMD.indexOf(this.two) !== -1) {
this.basic_cmd(line); this.basic_cmd(line);
} else if (startsWith(line, '@') || line.indexOf('@') !== -1) { }
else if (startsWith(line, '@') || line.indexOf('@') !== -1) {
this.variable(line); this.variable(line);
} else if (startsWith(line, 'LOOP')) { }
else if (startsWith(line, 'LOOP')) {
this.new_loop(line); this.new_loop(line);
} else if (startsWith(line, 'L ')) { }
else if (startsWith(line, 'L ')) {
this.light_state(line); this.light_state(line);
} else if (startsWith(line, 'F ')) { }
else if (startsWith(line, 'F ')) {
this.new_loop(line, true); this.new_loop(line, true);
} else if (startsWith(line, 'END')) { }
else if (startsWith(line, 'END')) {
this.end_loop(line); this.end_loop(line);
} else if (startsWith(line, 'CAM')) { //directly go to that frame (black?) }
else if (startsWith(line, 'CAM')) { //directly go to that frame (black?)
this.move_cam(line); this.move_cam(line);
} else if (startsWith(line, 'PROJ')) { //directly go to that frame }
else if (startsWith(line, 'PROJ')) { //directly go to that frame
this.move_proj(line); this.move_proj(line);
} else if (startsWith(line, 'SET')) { //set that state }
else if (startsWith(line, 'SET')) { //set that state
this.set_state(line); this.set_state(line);
} else if (startsWith(line, '#') || startsWith(line, '//')) { }
else if (startsWith(line, '#') || startsWith(line, '//')) {
//comments //comments
//ignore while parsing //ignore while parsing
} }
} }
this.output.success = true; this.output.success = true;
this.output.arr = this.arr; //all instructions this.output.arr = this.arr; //all instructions
this.output.light = this.light; //all light instructions this.output.light = this.light; //all light instructions
this.output.cam = this.cam; this.output.cam = this.cam;
this.output.proj = this.proj; this.output.proj = this.proj;
if (typeof callback !== 'undefined') { if (typeof callback !== 'undefined') {
//should only be invoked by running mscript.tests() //should only be invoked by running mscript.tests()
callback(this.output); callback(this.output);
} else { }
else {
return this.output; return this.output;
} }
} }
variable (line) { variable(line) {
let parts = line.split('='); let parts = line.split('=');
let key = parts[0]; let key = parts[0];
let value = parts[1]; let value = parts[1];
let update = false; let update = false;
if (value && value.indexOf('#') !== -1) { if (value && value.indexOf('#') !== -1) {
value = value.split('#')[0]; value = value.split('#')[0];
} }
if (value && value.indexOf('//') !== -1) { if (value && value.indexOf('//') !== -1) {
value = value.split('//')[0]; value = value.split('//')[0];
} }
if (value && value.indexOf('+') !== -1) { if (value && value.indexOf('+') !== -1) {
if (value) if (value)
update = true; update = true;
} }
if (line.indexOf('-') !== -1) { if (line.indexOf('-') !== -1) {
update = true; update = true;
} }
if (line.indexOf(',') === -1) { //if not color string if (line.indexOf(',') === -1) { //if not color string
try { try {
value = parseInt(value); value = parseInt(value);
} catch (err) { }
catch (err) {
//supress parsing error //supress parsing error
} }
} }
@ -161,41 +162,42 @@ class Mscript {
if (!this.variables[key] || update) { if (!this.variables[key] || update) {
this.variables[key] = value; this.variables[key] = value;
} }
console.dir(this.variables) //console.dir(this.variables)
} }
variable_replace(line) { variable_replace(line) {
} }
/** /**
* Apply a basic two character command * Interpret a basic two character command
*
* @param line {string} Line of script to interpret
*/ */
basic_cmd (line) { basic_cmd(line) {
if (this.rec !== -1) { if (this.rec !== -1) {
//hold generated arr in state loop array //hold generated arr in state loop array
this.loops[this.rec].arr this.loops[this.rec].arr
.push.apply(this.loops[this.rec].arr, .push.apply(this.loops[this.rec].arr, this.str_to_arr(line, this.two));
this.str_to_arr(line,
this.two));
this.loops[this.rec].light this.loops[this.rec].light
.push.apply(this.loops[this.rec].light, .push.apply(this.loops[this.rec].light, this.light_to_arr(line, this.two));
this.light_to_arr(line, }
this.two)); else {
} else {
this.arr.push.apply(this.arr, this.str_to_arr(line, this.two)); this.arr.push.apply(this.arr, this.str_to_arr(line, this.two));
this.light.push.apply(this.light, this.light_to_arr(line, this.two)) this.light.push.apply(this.light, this.light_to_arr(line, this.two));
} }
} }
/** /**
* Start a new loop * Start a new loop
*
* @param line {string} Line to evaluate as either loop or fade
* @param fade {boolean} Flag as boolean if true
*/ */
new_loop (line, fade) { new_loop(line, fade) {
this.rec++; this.rec++;
this.loops[this.rec] = { this.loops[this.rec] = {
arr : [], arr: [],
light : [], light: [],
cam : 0, cam: 0,
proj : 0, proj: 0,
cmd : line + '' cmd: line + ''
}; };
if (fade) { if (fade) {
this.fade(line); this.fade(line);
@ -203,13 +205,14 @@ class Mscript {
} }
/** /**
* Close the most recent loop * Close the most recent loop
*
* @param line {string} Line to interpret
*/ */
end_loop (line) { end_loop(line) {
let light_arr; let light_arr;
let start; let start;
let end; let end;
let len; let len;
for (let x = 0; x < this.loop_count(this.loops[this.rec].cmd); x++) { for (let x = 0; x < this.loop_count(this.loops[this.rec].cmd); x++) {
light_arr = this.loops[this.rec].light; light_arr = this.loops[this.rec].light;
if (this.loops[this.rec].fade) { if (this.loops[this.rec].fade) {
@ -218,19 +221,17 @@ class Mscript {
len = this.loops[this.rec].fade_len; len = this.loops[this.rec].fade_len;
light_arr = light_arr.map(l => { light_arr = light_arr.map(l => {
return this.fade_rgb(start, end, len, x); return this.fade_rgb(start, end, len, x);
}) });
} }
if (this.rec === 0) { if (this.rec === 0) {
this.arr.push.apply(this.arr, this.loops[this.rec].arr); this.arr.push.apply(this.arr, this.loops[this.rec].arr);
this.light.push.apply(this.light, light_arr); this.light.push.apply(this.light, light_arr);
} else if (this.rec >= 1) { }
else if (this.rec >= 1) {
this.loops[this.rec - 1].arr this.loops[this.rec - 1].arr
.push.apply(this.loops[this.rec - 1].arr, .push.apply(this.loops[this.rec - 1].arr, this.loops[this.rec].arr);
this.loops[this.rec].arr);
this.loops[this.rec - 1].light this.loops[this.rec - 1].light
.push.apply(this.loops[this.rec - 1].light, .push.apply(this.loops[this.rec - 1].light, light_arr);
light_arr);
} }
} }
this.update('END', this.loop_count(this.loops[this.rec].cmd)); this.update('END', this.loop_count(this.loops[this.rec].cmd));
@ -239,8 +240,10 @@ class Mscript {
} }
/** /**
* Move camera to explicitly-defined frame * Move camera to explicitly-defined frame
*
* @param line {string} Line to interpret with camera move statement
*/ */
move_cam (line) { move_cam(line) {
this.target = parseInt(line.split('CAM ')[1]); this.target = parseInt(line.split('CAM ')[1]);
if (this.rec !== -1) { if (this.rec !== -1) {
if (this.target > this.cam) { if (this.target > this.cam) {
@ -250,7 +253,8 @@ class Mscript {
this.loops[this.rec].light.push(BLACK); this.loops[this.rec].light.push(BLACK);
this.update('BF'); this.update('BF');
} }
} else { }
else {
this.dist = this.cam - this.target; this.dist = this.cam - this.target;
for (let x = 0; x < this.dist; x++) { for (let x = 0; x < this.dist; x++) {
this.loops[this.rec].arr.push('BB'); this.loops[this.rec].arr.push('BB');
@ -258,7 +262,8 @@ class Mscript {
this.update('BB'); this.update('BB');
} }
} }
} else { }
else {
if (this.target > this.cam) { if (this.target > this.cam) {
this.dist = this.target - this.cam; this.dist = this.target - this.cam;
for (let x = 0; x < this.dist; x++) { for (let x = 0; x < this.dist; x++) {
@ -266,7 +271,8 @@ class Mscript {
this.light.push(BLACK); this.light.push(BLACK);
this.update('BF'); this.update('BF');
} }
} else { }
else {
this.dist = this.cam - this.target; this.dist = this.cam - this.target;
for (let x = 0; x < this.dist; x++) { for (let x = 0; x < this.dist; x++) {
this.arr.push('BB'); this.arr.push('BB');
@ -279,7 +285,7 @@ class Mscript {
/** /**
* Move projector to explicitly-defined frame * Move projector to explicitly-defined frame
*/ */
move_proj (line) { move_proj(line) {
this.target = parseInt(line.split('PROJ ')[1]); this.target = parseInt(line.split('PROJ ')[1]);
if (this.rec !== -1) { if (this.rec !== -1) {
if (this.target > this.proj) { if (this.target > this.proj) {
@ -289,7 +295,8 @@ class Mscript {
this.loops[this.rec].light.push(''); this.loops[this.rec].light.push('');
this.update('PF'); this.update('PF');
} }
} else { }
else {
this.dist = this.proj - this.target; this.dist = this.proj - this.target;
for (let x = 0; x < this.dist; x++) { for (let x = 0; x < this.dist; x++) {
this.loops[this.rec].arr.push('PB'); this.loops[this.rec].arr.push('PB');
@ -297,7 +304,8 @@ class Mscript {
this.update('PB'); this.update('PB');
} }
} }
} else { }
else {
if (this.target > this.proj) { if (this.target > this.proj) {
this.dist = this.target - this.proj; this.dist = this.target - this.proj;
for (let x = 0; x < this.dist; x++) { for (let x = 0; x < this.dist; x++) {
@ -305,7 +313,8 @@ class Mscript {
this.light.push(''); this.light.push('');
this.update('PF'); this.update('PF');
} }
} else { }
else {
this.dist = this.proj - this.target; this.dist = this.proj - this.target;
for (let x = 0; x < this.dist; x++) { for (let x = 0; x < this.dist; x++) {
this.arr.push('PB'); this.arr.push('PB');
@ -318,39 +327,39 @@ class Mscript {
/** /**
* Set the state of either the cam or projector * Set the state of either the cam or projector
*/ */
set_state (line) { set_state(line) {
if (startsWith(line, 'SET CAM')) { if (startsWith(line, 'SET CAM')) {
this.cam = parseInt(line.split('SET CAM')[1]); this.cam = parseInt(line.split('SET CAM')[1]);
} else if (startsWith(line, 'SET PROJ')) { }
else if (startsWith(line, 'SET PROJ')) {
this.proj = parseInt(line.split('SET PROJ')[1]); this.proj = parseInt(line.split('SET PROJ')[1]);
} }
} }
/** /**
* Return the last loop * Return the last loop
*/ */
last_loop () { last_loop() {
return this.loops[this.loops.length - 1]; return this.loops[this.loops.length - 1];
} }
/** /**
* Return the second-last loop * Return the second-last loop
*/ */
parent_loop () { parent_loop() {
return this.loops[this.loops.length - 2]; return this.loops[this.loops.length - 2];
} }
/** /**
* Extract the loop count integer from a LOOP cmd * Extract the loop count integer from a LOOP cmd
*/ */
loop_count (str) { loop_count(str) {
return parseInt(str.split(' ')[1]); return parseInt(str.split(' ')[1]);
} }
/** /**
* Execute a fade of frame length, from color to another color * Execute a fade of frame length, from color to another color
*/ */
fade (line) { fade(line) {
let len = this.fade_count(line); let len = this.fade_count(line);
let start = this.fade_start(line); let start = this.fade_start(line);
let end = this.fade_end(line); let end = this.fade_end(line);
this.loops[this.rec].start = start; this.loops[this.rec].start = start;
this.loops[this.rec].end = end; this.loops[this.rec].end = end;
this.loops[this.rec].fade = true; this.loops[this.rec].fade = true;
@ -360,115 +369,133 @@ class Mscript {
/** /**
* Extract the fade length integer from a FADE cmd * Extract the fade length integer from a FADE cmd
*/ */
fade_count (str) { fade_count(str) {
return parseInt(str.split(' ')[1]); return parseInt(str.split(' ')[1]);
} }
/** /**
* Extract the start color from a string * Extract the start color from a string
*/ */
fade_start (str) { fade_start(str) {
let color = str.split(' ')[2]; let color = str.split(' ')[2];
return this.rgb(color.trim()) return this.rgb(color.trim());
} }
/** /**
* Extract the end color from a string * Extract the end color from a string
*/ */
fade_end (str) { fade_end(str) {
let color = str.split(' ')[3]; let color = str.split(' ')[3];
return this.rgb(color.trim()) return this.rgb(color.trim());
} }
fade_rgb (start, end, len, x) { fade_rgb(start, end, len, x) {
let cur = []; let cur = [];
let diff; let diff;
for (let i = 0; i < 3; i++) { for (let i = 0; i < 3; i++) {
if (x === len - 1) { if (x === len - 1) {
cur[i] = end[i]; cur[i] = end[i];
} else if (start[i] >= end[i]) { }
else if (start[i] >= end[i]) {
diff = start[i] - end[i]; diff = start[i] - end[i];
cur[i] = start[i] - Math.round((diff / (len - 1)) * x); cur[i] = start[i] - Math.round((diff / (len - 1)) * x);
} else { }
else {
diff = end[i] - start[i]; diff = end[i] - start[i];
cur[i] = start[i] + Math.round((diff / (len - 1)) * x); cur[i] = start[i] + Math.round((diff / (len - 1)) * x);
} }
} }
return this.rgb_str(cur); return this.rgb_str(cur);
} }
rgb (str) { rgb(str) {
let rgb = str.split(','); let rgb = str.split(',');
return rgb.map( char => { return rgb.map((char) => {
return parseInt(char); return parseInt(char);
}) });
} }
rgb_str (arr) { /**
*
**/
rgb_str(arr) {
return arr.join(','); return arr.join(',');
} }
/** /**
* Increase the state of a specific object, such as the camera/projector, * Increase the state of a specific object, such as the camera/projector,
* by the value defined in val * by the value defined in val
*/ */
update (cmd, val = 1) { update(cmd, val = 1) {
if (cmd === 'END') { if (cmd === 'END') {
//I don't understand this loop //I don't understand this loop
for (let i = 0; i < val; i++) { for (let i = 0; i < val; i++) {
if (this.rec === 0) { if (this.rec === 0) {
this.cam += this.loops[this.rec].cam; this.cam += this.loops[this.rec].cam;
this.proj += this.loops[this.rec].proj; this.proj += this.loops[this.rec].proj;
} else if (this.rec >= 1) { }
else if (this.rec >= 1) {
this.loops[this.rec - 1].cam += this.loops[this.rec].cam; this.loops[this.rec - 1].cam += this.loops[this.rec].cam;
this.loops[this.rec - 1].proj += this.loops[this.rec].proj; this.loops[this.rec - 1].proj += this.loops[this.rec].proj;
} }
} }
} else if (cmd === 'CF') { }
else if (cmd === 'CF') {
if (this.rec === -1) { if (this.rec === -1) {
this.cam += val; this.cam += val;
} else { }
else {
this.loops[this.rec].cam += val; this.loops[this.rec].cam += val;
} }
} else if (cmd === 'CB') { }
else if (cmd === 'CB') {
if (this.rec === -1) { if (this.rec === -1) {
this.cam -= val; this.cam -= val;
} else { }
else {
this.loops[this.rec].cam--; this.loops[this.rec].cam--;
} }
} else if (cmd === 'PF') { }
else if (cmd === 'PF') {
if (this.rec === -1) { if (this.rec === -1) {
this.proj += val; this.proj += val;
} else { }
else {
this.loops[this.rec].proj += val; this.loops[this.rec].proj += val;
} }
} else if (cmd === 'PB') { }
else if (cmd === 'PB') {
if (this.rec === -1) { if (this.rec === -1) {
this.proj -= val; this.proj -= val;
} else { }
else {
this.loops[this.rec].proj--; this.loops[this.rec].proj--;
} }
} else if (cmd === 'BF') { }
else if (cmd === 'BF') {
if (this.rec === -1) { if (this.rec === -1) {
this.cam += val; this.cam += val;
} else { }
else {
this.loops[this.rec].cam += val; this.loops[this.rec].cam += val;
} }
} else if (cmd === 'BB') { }
else if (cmd === 'BB') {
if (this.rec === -1) { if (this.rec === -1) {
this.cam -= val; this.cam -= val;
} else { }
else {
this.loops[this.rec].cam -= val; this.loops[this.rec].cam -= val;
} }
} else if (cmd === 'L ') { }
else if (cmd === 'L ') {
} }
} }
/** /**
* Split string on command, extract any integers from string * Split string on command, extract any integers from string
*/ */
str_to_arr (str, cmd) { str_to_arr(str, cmd) {
const cnt = str.split(cmd); const cnt = str.split(cmd);
let c = parseInt(cnt[1]); let c = parseInt(cnt[1]);
let arr = []; let arr = [];
if (cnt[1] === '') { if (cnt[1] === '') {
c = 1; c = 1;
} else { }
else {
c = parseInt(cnt[1]); c = parseInt(cnt[1]);
} }
arr = new Array(c).fill(cmd); arr = new Array(c).fill(cmd);
@ -478,23 +505,26 @@ class Mscript {
/** /**
* Split a string on a command to extract data for light array * Split a string on a command to extract data for light array
*/ */
light_to_arr (str, cmd) { light_to_arr(str, cmd) {
const cnt = str.split(cmd); const cnt = str.split(cmd);
let c = parseInt(cnt[1]); let c = parseInt(cnt[1]);
let arr = []; let arr = [];
if (cnt[1] === '') { if (cnt[1] === '') {
c = 1; c = 1;
} else { }
else {
c = parseInt(cnt[1]); c = parseInt(cnt[1]);
} }
for (var i = 0; i < c; i++) { for (let i = 0; i < c; i++) {
if (cmd === 'CF' if (cmd === 'CF'
|| cmd === 'CB') { || cmd === 'CB') {
arr.push(this.color); arr.push(this.color);
} else if (cmd === 'BF' }
else if (cmd === 'BF'
|| cmd === 'BB') { || cmd === 'BB') {
arr.push(BLACK); arr.push(BLACK);
} else { }
else {
arr.push(''); arr.push('');
} }
} }
@ -503,43 +533,19 @@ class Mscript {
/** /**
* Split a string to extract an rgb color value * Split a string to extract an rgb color value
*/ */
light_state (str) { light_state(str) {
//add parsers for other color spaces //add parsers for other color spaces
const color = str.replace('L ', '').trim(); const color = str.replace('L ', '').trim();
this.color = color; this.color = color;
} }
/** /**
* Throw an error with specific message * Throw an error with specific message
*
* @param msg {string} Error message to print
*/ */
fail (msg) { fail(msg) {
throw new Error(msg); throw new Error(msg);
} }
} }
module.exports = Mscript; module.exports = Mscript;
//# sourceMappingURL=index.js.map
/*
CAM # - go to camera frame #
PROJ # - go to projector frame #
SET CAM # - sets camera count to #
SET PROJ # - sets projector 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
CF - Camera forwards
PF - Projector forwards
BF - Black forwards
CB - Camera backwards
PB - Projector backwards
BB - Black backwards
*/

1
lib/mscript/index.js.map Normal file

File diff suppressed because one or more lines are too long