From ced885d03fbcff6966a576d760aeb4ffdf571fe1 Mon Sep 17 00:00:00 2001 From: Mathura MG Date: Sat, 12 Nov 2016 11:53:02 -0500 Subject: [PATCH] Add multiple canvas text outputs (#197) * added grid * add table to textoutput * add all three outputs * add shortcuts * fix sound bug for triangle * fix triangle bug - again --- .../IDE/components/KeyboardShortcutModal.jsx | 2 +- client/modules/IDE/components/Preferences.jsx | 34 +- .../modules/IDE/components/PreviewFrame.jsx | 23 +- client/modules/IDE/components/TextOutput.jsx | 9 +- client/modules/IDE/pages/IDEView.jsx | 11 +- client/modules/IDE/reducers/preferences.js | 2 +- server/models/user.js | 2 +- static/{ => interceptor}/data.min.json | 0 .../gridInterceptor/intercept-p5.js | 60 +++ .../gridInterceptor/interceptor-functions.js | 349 ++++++++++++++++++ .../intercept-helper-functions.js | 0 static/{ => interceptor}/loadData.js | 2 +- static/{ => interceptor}/ntc.min.js | 0 .../soundInterceptor/intercept-p5.js | 130 +++++++ .../textInterceptor}/intercept-p5.js | 11 +- .../textInterceptor}/interceptor-functions.js | 1 - 16 files changed, 609 insertions(+), 27 deletions(-) rename static/{ => interceptor}/data.min.json (100%) create mode 100644 static/interceptor/gridInterceptor/intercept-p5.js create mode 100644 static/interceptor/gridInterceptor/interceptor-functions.js rename static/{ => interceptor}/intercept-helper-functions.js (100%) rename static/{ => interceptor}/loadData.js (88%) rename static/{ => interceptor}/ntc.min.js (100%) create mode 100644 static/interceptor/soundInterceptor/intercept-p5.js rename static/{ => interceptor/textInterceptor}/intercept-p5.js (84%) rename static/{ => interceptor/textInterceptor}/interceptor-functions.js (99%) diff --git a/client/modules/IDE/components/KeyboardShortcutModal.jsx b/client/modules/IDE/components/KeyboardShortcutModal.jsx index a0a3865b..a500b4e4 100644 --- a/client/modules/IDE/components/KeyboardShortcutModal.jsx +++ b/client/modules/IDE/components/KeyboardShortcutModal.jsx @@ -60,7 +60,7 @@ class KeyboardShortcutModal extends React.Component { {this.isMac ? 'Command + Shift + 1' : 'Control + Shift + 1'} - Turn On Text-based Canvas + Toggle Text-based Canvas
  • diff --git a/client/modules/IDE/components/Preferences.jsx b/client/modules/IDE/components/Preferences.jsx index 11b840ba..61688a0c 100644 --- a/client/modules/IDE/components/Preferences.jsx +++ b/client/modules/IDE/components/Preferences.jsx @@ -264,24 +264,46 @@ class Preferences extends React.Component {
    this.props.setTextOutput(true)} + onChange={() => this.props.setTextOutput(1)} aria-label="text output on" name="text output" id="text-output-on" className="preference__radio-button" value="On" - checked={this.props.textOutput} + checked={Boolean(this.props.textOutput === 1)} /> - + this.props.setTextOutput(false)} + onChange={() => this.props.setTextOutput(2)} + aria-label="grid output on" + name="grid output" + id="grid-output-on" + className="preference__radio-button" + value="Grid On" + checked={Boolean(this.props.textOutput === 2)} + /> + + this.props.setTextOutput(3)} + aria-label="sound output on" + name="sound output" + id="sound-output-on" + className="preference__radio-button" + value="On" + checked={Boolean(this.props.textOutput === 3)} + /> + + this.props.setTextOutput(0)} aria-label="text output off" name="text output" id="text-output-off" className="preference__radio-button" value="Off" - checked={!this.props.textOutput} + checked={!Boolean(this.props.textOutput)} /> @@ -304,7 +326,7 @@ Preferences.propTypes = { setFontSize: PropTypes.func.isRequired, autosave: PropTypes.bool.isRequired, setAutosave: PropTypes.func.isRequired, - textOutput: PropTypes.bool.isRequired, + textOutput: PropTypes.number.isRequired, setTextOutput: PropTypes.func.isRequired, lintWarning: PropTypes.bool.isRequired, setLintWarning: PropTypes.func.isRequired, diff --git a/client/modules/IDE/components/PreviewFrame.jsx b/client/modules/IDE/components/PreviewFrame.jsx index d53021f1..1f39d492 100644 --- a/client/modules/IDE/components/PreviewFrame.jsx +++ b/client/modules/IDE/components/PreviewFrame.jsx @@ -260,12 +260,21 @@ class PreviewFrame extends React.Component { htmlHeadContents = htmlHeadContents.slice(1, htmlHeadContents.length - 2); htmlHeadContents += '\n'; - if (this.props.textOutput || this.props.isTextOutputPlaying) { - htmlHeadContents += '\n'; - htmlHeadContents += '\n'; - htmlHeadContents += '\n'; - htmlHeadContents += '\n'; - htmlHeadContents += ''; + if (this.props.textOutput === 1 || this.props.isTextOutputPlaying) { + htmlHeadContents += '\n'; + htmlHeadContents += '\n'; + htmlHeadContents += '\n'; + htmlHeadContents += '\n'; + htmlHeadContents += ''; + } else if (this.props.textOutput === 2 || this.props.isTextOutputPlaying) { + htmlHeadContents += '\n'; + htmlHeadContents += '\n'; + htmlHeadContents += '\n'; + htmlHeadContents += '\n'; + htmlHeadContents += ''; + } else if (this.props.textOutput === 3 || this.props.isTextOutputPlaying) { + htmlHeadContents += '\n'; + htmlHeadContents += '\n'; } htmlFile = htmlFile.replace(/(?:)([\s\S]*?)(?:<\/head>)/gmi, `\n${htmlHeadContents}\n`); @@ -317,7 +326,7 @@ class PreviewFrame extends React.Component { PreviewFrame.propTypes = { isPlaying: PropTypes.bool.isRequired, isTextOutputPlaying: PropTypes.bool.isRequired, - textOutput: PropTypes.bool.isRequired, + textOutput: PropTypes.number.isRequired, content: PropTypes.string, htmlFile: PropTypes.shape({ content: PropTypes.string.isRequired diff --git a/client/modules/IDE/components/TextOutput.jsx b/client/modules/IDE/components/TextOutput.jsx index 777dd370..b33343f6 100644 --- a/client/modules/IDE/components/TextOutput.jsx +++ b/client/modules/IDE/components/TextOutput.jsx @@ -27,10 +27,17 @@ class TextOutput extends React.Component {
    +
    +
    ); } diff --git a/client/modules/IDE/pages/IDEView.jsx b/client/modules/IDE/pages/IDEView.jsx index 2876f49c..0f2a654c 100644 --- a/client/modules/IDE/pages/IDEView.jsx +++ b/client/modules/IDE/pages/IDEView.jsx @@ -169,10 +169,15 @@ class IDEView extends React.Component { this.props.startSketchAndRefresh(); } else if (e.keyCode === 50 && ((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac)) && e.shiftKey) { e.preventDefault(); - this.props.setTextOutput(false); + this.props.setTextOutput(0); } else if (e.keyCode === 49 && ((e.metaKey && this.isMac) || (e.ctrlKey && !this.isMac)) && e.shiftKey) { e.preventDefault(); - this.props.setTextOutput(true); + if (this.props.preferences.textOutput === 3) { + this.props.preferences.textOutput = 1; + } else { + this.props.preferences.textOutput += 1; + } + this.props.setTextOutput(this.props.preferences.textOutput); } } @@ -534,7 +539,7 @@ IDEView.propTypes = { isTabIndent: PropTypes.bool.isRequired, autosave: PropTypes.bool.isRequired, lintWarning: PropTypes.bool.isRequired, - textOutput: PropTypes.bool.isRequired, + textOutput: PropTypes.number.isRequired, theme: PropTypes.string.isRequired, autorefresh: PropTypes.bool.isRequired }).isRequired, diff --git a/client/modules/IDE/reducers/preferences.js b/client/modules/IDE/reducers/preferences.js index 219c2b31..f4161670 100644 --- a/client/modules/IDE/reducers/preferences.js +++ b/client/modules/IDE/reducers/preferences.js @@ -6,7 +6,7 @@ const initialState = { isTabIndent: true, autosave: true, lintWarning: false, - textOutput: false, + textOutput: 0, theme: 'light', autorefresh: false }; diff --git a/server/models/user.js b/server/models/user.js index 6cd067e7..c88fef8f 100644 --- a/server/models/user.js +++ b/server/models/user.js @@ -17,7 +17,7 @@ const userSchema = new Schema({ isTabIndent: { type: Boolean, default: false }, autosave: { type: Boolean, default: true }, lintWarning: { type: Boolean, default: false }, - textOutput: { type: Boolean, default: false }, + textOutput: { type: Number, default: 0 }, theme: { type: String, default: 'light' }, autorefresh: { type: Boolean, default: false } } diff --git a/static/data.min.json b/static/interceptor/data.min.json similarity index 100% rename from static/data.min.json rename to static/interceptor/data.min.json diff --git a/static/interceptor/gridInterceptor/intercept-p5.js b/static/interceptor/gridInterceptor/intercept-p5.js new file mode 100644 index 00000000..ae638d95 --- /dev/null +++ b/static/interceptor/gridInterceptor/intercept-p5.js @@ -0,0 +1,60 @@ +var canvasLocation =''; +var shadowDOMElement; + +funcNames = allData["classitems"].map(function(x){ + if(x["overloads"]) { + tempParam = x["overloads"][0]["params"]; + } else { + tempParam = x["params"]; + } + return { + name: x["name"], + params: tempParam, + class: x["class"], + module: x["module"], + submodule: x["submodule"] + }; +}); + +funcNames = funcNames.filter(function(x) { + var className = x["class"]; + return (x["name"] && x["params"] && (className==='p5')); +}) + + +funcNames.forEach(function(x){ + var document = parent.document; + var originalFunc = p5.prototype[x.name]; + p5.prototype[x.name] = function(){ + orgArg = arguments; + if(frameCount == 0) { //for setup + document.getElementById('textOutput-content-table').innerHTML = ''; + document.getElementById('textOutput-content-details').innerHTML = ''; + document.getElementById('textOutput-content-summary').innerHTML = ''; + Interceptor.createShadowDOMElement(document); + Interceptor.setupObject = Interceptor.populateObject(x,arguments, Interceptor.setupObject, document.getElementById('textOutput-content-details'),false); + Interceptor.populateObjectDetails(Interceptor.setupObject,Interceptor.drawObject,document.getElementById('textOutput-content-summary'),document.getElementById('textOutput-content-details')); + var table = document.getElementById('textOutput-content-details'); + Interceptor.populateTable(table,Interceptor.setupObject); + } + + else if(frameCount%50 == 0 ) { + Interceptor.drawObject = Interceptor.populateObject(x,arguments, Interceptor.drawObject, document.getElementById('textOutput-content-details'),true); + + Interceptor.isCleared = false; + } + //reset some of the variables + else if(frameCount%50 == 1 ) { + if(!Interceptor.isCleared){ + var cells = document.getElementsByClassName('textOutput-cell-content'); + for( i =0;i240) { + return 'white'; + } + else { + return 'grey'; + } + } + else if(!(typeof(arguments[0])).localeCompare("string")) { + if(!arguments[0].charAt(0).localeCompare('#')) { + //if user has entered a hex color + var n_match = ntc.name(arguments[0]); + return n_match[1]; + } + else { + return arguments[0]; + } + } + } + }, + + /* return which part of the canvas an object os present */ + canvasAreaLocation : function(x,arguments,canvasX,canvasY){ + + var x_loc,y_loc; + + for(var i=0;i-1) { + x_loc = a; + } + else if(x.params[i].description.indexOf("y-coordinate")>-1) { + y_loc = a; + } + } + + if(x_loc<0.4*canvasX) { + if(y_loc<0.4*canvasY) { + return 'top left'; + } + else if(y_loc>0.6*canvasY) { + return 'bottom left'; + } + else { + return 'mid left'; + } + } + else if(x_loc>0.6*canvasX) { + if(y_loc<0.4*canvasY) { + return 'top right'; + } + else if(y_loc>0.6*canvasY) { + return 'bottom right'; + } + else { + return 'mid right'; + } + } + else { + if(y_loc<0.4*canvasY) { + return 'top middle'; + } + else if(y_loc>0.6*canvasY) { + return 'bottom middle'; + } + else { + return 'middle'; + } + } + }, + + /* return which part of the canvas an object os present */ + canvasLocator : function(x,arguments,canvasX,canvasY){ + var x_loc, y_loc; + var locX, locY; + for(var i=0;i-1) { + x_loc = a; + } + else if(x.params[i].description.indexOf("y-coordinate")>-1) { + y_loc = a; + } + } + + locX = Math.floor((x_loc/canvasX)*this.noRows); + locY = Math.floor((y_loc/canvasY)*this.noCols); + if( locX == this.noRows) { + locX = locX - 1; + } + if( locY == this.noCols) { + locY = locY - 1; + } + return({ + locX: locX, + locY: locY + }) + + }, + + clearVariables : function(object) { + object.objectTypeCount = {}; + object.objectCount = 0; + this.isCleared = true; + return object; + }, + + createShadowDOMElement : function(document) { + var contentTable = document.getElementById('textOutput-content-table'); + for(var i=0; i createCanvas + if(!x.name.localeCompare('createCanvas')) { + this.canvasDetails.width = arguments[0]; + this.canvasDetails.height = arguments[1]; + } + } + /* Here - most of the functions are generalised, but some need specific outputs */ + + /* for `fill` function */ + if(!x.name.localeCompare('fill')) { + this.currentColor = this.getColorName(arguments); + } + + /* for `background` function */ + else if(!x.name.localeCompare('background')) { + this.bgColor = this.getColorName(arguments); + } + + /* for 2D functions and text function */ + else if(!x.module.localeCompare('Shape') || !x.module.localeCompare('Typography') &&((!x.submodule)||(x.submodule.localeCompare('Attributes')!=0)) ){ + this.objectArea = this.getObjectArea(x.name, arguments); + var canvasLocation = this.canvasAreaLocation(x, arguments ,width,height); + this.coordLoc = this.canvasLocator(x,arguments,width,height); + + /* in case of text, the description should be what is in the content */ + if(x.name.localeCompare('text')){ + this.objectDescription = x.name; + } + else { + this.objectDescription = arguments[0].substring(0,20); + } + objectArray[objectCount] = { + 'type' : this.currentColor + ' - ' + this.objectDescription , + 'location': canvasLocation, //top left vs top right etc + 'coordLoc': this.coordLoc, // 3,3 vs 5,3 etc + 'area': this.objectArea, + 'co-ordinates': this.coordinates // coordinates of where the objects are drawn + }; + this.coordinates = []; + + + /*add the object(shape/text) parameters in objectArray */ + for(var i=0;i-1) { + objectArray[objectCount]['co-ordinates'].push(arguments[i]+'x') + } + else if(x.params[i].description.indexOf("y-coordinate")>-1) { + objectArray[objectCount]['co-ordinates'].push(arguments[i]+'y') + } + else{ + objectArray[objectCount][x.params[i].description]=arguments[i]; + } + + } + if(objectTypeCount[x.name]) { + objectTypeCount[x.name]++; + } + else { + objectTypeCount[x.name]=1; + } + objectCount++; + } + return ({ + objectCount : objectCount, + objectArray : objectArray, + objectTypeCount : objectTypeCount + }); + }, + + populateTable : function(objectArray,document_passed) { + if(this.totalCount<100) { + for(var i=0;i 1 ) { + elementSummary.innerHTML += ' Contains ' + this.totalCount + ' objects - '; + } + else { + elementSummary.innerHTML += ' Contains ' + this.totalCount + ' object - '; + } + + + if(object2.objectCount>0 || object1.objectCount>0 ) { + + totObjectTypeCount = MergeObjRecursive(object1.objectTypeCount, object2.objectTypeCount); + var keys = Object.keys(totObjectTypeCount); + for(var i=0;i-1){ + xPosPrev = arguments[i]; + xPosCurr = arguments[i]; + } + if(x.params[i].description.indexOf('y-coordinate')>-1){ + yPosPrev = arguments[i]; + yPosCurr = arguments[i]; + } + } + } + // Pull out only the shapes in draw() + else if(frameCount > 1 && (frameCount%1 == 0) && (x.module.localeCompare('Shape') === 0)) { + if(frameCount != currFrame) { + currFrame ++; + objectCount = 0; + } + objectCount ++; + + if(!objects[objectCount-1]){ + objects[objectCount-1] = new Object({ + xPosCurr:0, + xPosDiff:0, + xPosPrev:0, + yPosCurr:0, + yPosDiff:0, + yPosPrev:0 + }); + } + //pull out only the x coord values and compare with prev value + for(var i =0; i < x.params.length; i++) { + if(x.params[i].description.indexOf('y-coordinate')>-1){ + objects[objectCount-1].yPosCurr = arguments[i]; + objects[objectCount-1].yPosDiff = objects[objectCount-1].yPosCurr - objects[objectCount-1].yPosPrev; + objects[objectCount-1].yPosPrev = objects[objectCount-1].yPosCurr; + break; + } + } + + for(var i =0; i < x.params.length; i++) { + if(x.params[i].description.indexOf('x-coordinate')>-1){ + objects[objectCount-1].xPosCurr = arguments[i]; + objects[objectCount-1].xPosDiff = objects[objectCount-1].xPosCurr - objects[objectCount-1].xPosPrev; + objects[objectCount-1].xPosPrev = objects[objectCount-1].xPosCurr; + break; + } + } + + if(abs(objects[objectCount-1].xPosDiff>0)||abs(objects[objectCount-1].yPosDiff>0)) + { + + currNote = (1-objects[objectCount-1].yPosCurr/height)*(12); // mapping hieghts to notes from 1-100 + //fn = f0 * (a)n + currLogFreq = 440 * Math.pow(Math.pow(2,(1/12)),currNote); + currVol = 0.4; + x_coord = frameCount%10 - 5; + currVol = 2*objectCount*Math.exp(-((x_coord+2*objectCount)*(x_coord+2*objectCount))); + currPan = (objects[objectCount-1].xPosCurr/width)*2 - 1; + oscillatorNode.frequency.value = currLogFreq; + gainNode.gain.value = currVol; + panNode.pan.value = currPan; + } + else { + gainNode.gain.value = 0; + } + } + return originalFunc.apply(this,arguments); + } +}); + +window.onload = function() { + oscillatorNode.type = 'sine'; + oscillatorNode.frequency.value = 440; // value in hertz + oscillatorNode.start(); + oscillatorNode.connect(gainNode); + gainNode.connect(panNode); + panNode.connect(audioCtx.destination); + gainNode.gain.value = 0; +} diff --git a/static/intercept-p5.js b/static/interceptor/textInterceptor/intercept-p5.js similarity index 84% rename from static/intercept-p5.js rename to static/interceptor/textInterceptor/intercept-p5.js index 81009c05..16870a30 100644 --- a/static/intercept-p5.js +++ b/static/interceptor/textInterceptor/intercept-p5.js @@ -1,4 +1,3 @@ -var textOutputElement; var canvasLocation =''; funcNames = allData["classitems"].map(function(x){ @@ -28,10 +27,12 @@ funcNames.forEach(function(x){ p5.prototype[x.name] = function(){ orgArg = arguments; if(frameCount == 0) { //for setup - Interceptor.setupObject = Interceptor.populateObject(x,arguments, Interceptor.setupObject, document.getElementById('textOutput-content-details'),false); + document.getElementById('textOutput-content-table').innerHTML = ''; + document.getElementById('textOutput-content-details').innerHTML = ''; + document.getElementById('textOutput-content-summary').innerHTML = ''; + Interceptor.setupObject = Interceptor.populateObject(x,arguments, Interceptor.setupObject, document.getElementById('textOutput-content-table'),false); Interceptor.getSummary(Interceptor.setupObject,Interceptor.drawObject,document.getElementById('textOutput-content-summary')); - var table = document.getElementById('textOutput-content-details'); - // table.innerHTML = ''; + var table = document.getElementById('textOutput-content-table'); Interceptor.populateTable(table,Interceptor.setupObject.objectArray); } @@ -42,7 +43,7 @@ funcNames.forEach(function(x){ //reset some of the variables else if(frameCount%100 == 1 ) { if(!Interceptor.isCleared){ - var table = document.getElementById('textOutput-content-details'); + var table = document.getElementById('textOutput-content-table'); Interceptor.getSummary(Interceptor.setupObject,Interceptor.drawObject,document.getElementById('textOutput-content-summary')); Interceptor.populateTable(table,Interceptor.setupObject.objectArray.concat(Interceptor.drawObject.objectArray)); } diff --git a/static/interceptor-functions.js b/static/interceptor/textInterceptor/interceptor-functions.js similarity index 99% rename from static/interceptor-functions.js rename to static/interceptor/textInterceptor/interceptor-functions.js index fb89e75c..c4a6af46 100644 --- a/static/interceptor-functions.js +++ b/static/interceptor/textInterceptor/interceptor-functions.js @@ -220,7 +220,6 @@ var Interceptor = { else{ // ie - there are fewer cols now for(var i =0;i