From 430a646e34dc430f06f5d49c5e919ad04fa749db Mon Sep 17 00:00:00 2001 From: TheTastefulToastie Date: Tue, 13 Nov 2018 00:16:35 +0000 Subject: [PATCH 1/5] Fix all the broken things... MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Man! This was so spaghetti'd I'm now Italian... ~ Removed a break from the switch statement in the constructor of CommandExecutor which meant that float values were pushed to the argument list twice (once as a float and then again as a command). ~ In Parser.parse() moved the call to nextToken() out of the else condition so that it is always executed. The intended functionality is to call parseExpression() when the argument type is int or float. After this change nextToken() was always executed regardless, then parseExpression was executed as well if the expression was an int or float, causing arguments to get skipped resulting in attempts to invoke numbers as if they were commands. ~ Renamed COMMAND_TYPES to ARGUMENT_TYPES but not everywhere causing ReferenceErrors ~ #30 Removed calls to parseInt() and parseFloat() in the CommandExecutor constructor because an argument may be an Expression object. #52 Added them back causing Expression objects to become NaN ~ Missing brace at end of parser.js ~ #50 also removed the calls to drawingBounds.reset() and drawingBounds.move(turtle.x, turtle.y) which obviously broke the autofit-drawing-to-canvas feature ~ The autofit-drawing-to-canvas feature was also broken because someone removed the call to the function ¯\_(ツ)_/¯ --- command.js | 19 ++++++++++++------- index.html | 11 ++++++----- parser.js | 25 ++++++++++++++----------- sketch.js | 52 +++++++++++++++++++++++++--------------------------- 4 files changed, 57 insertions(+), 50 deletions(-) diff --git a/command.js b/command.js index e37871f..619a13e 100644 --- a/command.js +++ b/command.js @@ -38,13 +38,17 @@ class CommandArg { if (validator === undefined) { switch (type) { case ARGUMENT_TYPES.INT: - this.validator = (str) => { - return /^\d+$/.test(str); + this.validator = (arg) => { + if (arg instanceof Expression) + arg = arg.eval(); + return /^\d+$/.test(arg); } break; case ARGUMENT_TYPES.FLOAT: - this.validator = (str) => { - return /^[-+]?[0-9]*\.?[0-9]*$/.test(str); + this.validator = (arg) => { + if (arg instanceof Expression) + arg = arg.eval(); + return /^[-+]?[0-9]*\.?[0-9]*$/.test(arg); } } } else @@ -105,10 +109,11 @@ class CommandExecutor { this.values.push(value); break; case ARGUMENT_TYPES.INT: - this.values.push(parseInt(value)); + this.values.push(value); break; case ARGUMENT_TYPES.FLOAT: - this.values.push(parseFloat(value)); + this.values.push(value); + break; case ARGUMENT_TYPES.COMMANDS: this.values.push( new Parser(value, this.callback).parse() @@ -135,7 +140,7 @@ class CommandExecutor { for(let i = 0; i < this.values.length; i++) { let val = this.values[i]; - if(this.command.argsTemplate[i].type == COMMAND_TYPES.FLOAT || this.command.argsTemplate[i].type == COMMAND_TYPES.INT) + if(this.command.argsTemplate[i].type == ARGUMENT_TYPES.FLOAT || this.command.argsTemplate[i].type == ARGUMENT_TYPES.INT) { values.push(val.eval(repcount)); } else { diff --git a/index.html b/index.html index dc41def..0585fd8 100644 --- a/index.html +++ b/index.html @@ -13,6 +13,7 @@ + @@ -33,22 +34,22 @@ - +
- + - + Icons made by Turtle from www.flaticon.com is licensed by CC 3.0 BY - + - + diff --git a/parser.js b/parser.js index 81e1862..b90afee 100644 --- a/parser.js +++ b/parser.js @@ -80,7 +80,7 @@ class Parser { e = new Expression(next, token, right); } else if(next == '+' || next == '-') { right = this.parseExpression(); - e = new Expression(next, token, right); + e = new Expression(next, token, right); } else { this.index = temp; return token; @@ -102,25 +102,28 @@ class Parser { parse() { let cmdsExecutors = []; while (this.remainingTokens()) { - let token = this.nextToken(); - let cmd = commandLookUp.get(token);; + let cmd = commandLookUp.get(token); if (cmd) { let args = []; for (let i = 0; i < cmd.argsTemplate.length; i++) { let startIndex = this.index; let arg = cmd.argsTemplate[i]; - let theArgToken = this.nextToken(); + let theArgToken; if(arg.type == ARGUMENT_TYPES.FLOAT || arg.type == ARGUMENT_TYPES.INT) { theArgToken = this.parseExpression(); - if(arg.validator !== undefined){ - if(!arg.validator(theArgToken)) - console.error(`Argument number ${i} (${theArgToken}) is invalid for command ${token}`); - args.push(theArgToken); - } - else { + if(arg.validator !== undefined){ + if(!arg.validator(theArgToken)) + console.error(`Argument number ${i} (${theArgToken}) is invalid for command ${token}`); + args.push(theArgToken); + } + else { + args.push(theArgToken); + console.warn(`A validator is missing for argument ${theArgToken}`); + } + } else { + theArgToken = this.nextToken(); args.push(theArgToken); - console.warn(`A validator is missing for argument ${theArgToken}`); } } cmdsExecutors.push(new CommandExecutor(cmd, args, this.afterCmdCallback)); diff --git a/sketch.js b/sketch.js index 1b758a5..cf62565 100644 --- a/sketch.js +++ b/sketch.js @@ -7,10 +7,10 @@ let turtle; let recentreBtn; let bgcolorBtn; +let dragStartMousePos = new p5.Vector(); +let dragStartCanvasOffset = new p5.Vector(); let xOffset = 0; let yOffset = 0; -let startX = 100; -let startY = 100; let allCases; let bgcolor = "#6040e6"; @@ -19,7 +19,7 @@ let canvasScrollX = 0; let canvasScrollY = 0; let canvasScaleX = 1; let canvasScaleY = 1; -let drawing_bounds = new BoundingBox(); +let drawingBounds = new BoundingBox(); let drawingPadding = 50; // Padding round the edge of the drawing when autofit function preload() { @@ -34,16 +34,16 @@ function setup() { angleMode(DEGREES); background(bgcolor); - + canvas.mousePressed(function () { - xOffset = mouseX-startX; - yOffset = mouseY-startY; + dragStartMousePos = new p5.Vector(mouseX, mouseY); + dragStartCanvasOffset = new p5.Vector(canvasScrollX, canvasScrollY); }); canvas.mouseMoved(function () { if (mouseIsPressed) { - startX = mouseX-xOffset; - startY = mouseY-yOffset; + canvasScrollX = dragStartCanvasOffset.x + dragStartMousePos.x - mouseX; + canvasScrollY = dragStartCanvasOffset.y + dragStartMousePos.y - mouseY; goTurtle(); } }); @@ -52,9 +52,7 @@ function setup() { bgcolorBtn = document.querySelector("#bgcolor"); recentreBtn.onclick = function () { - startX = width/2; - startY = height/2; - goTurtle(); + scaleToFitBoundingBox(drawingBounds); } bgcolorBtn.onclick = function () { @@ -69,41 +67,38 @@ function setup() { console.log(bgcolor); } - startX = width/2; - startY = height/2; editor = select("#code"); editor.input(goTurtle); - goTurtle(); + scaleToFitBoundingBox(drawingBounds); // This also redraws (it has to in order to measure the size of the drawing) } - + function scaleToFitBoundingBox(boundingBox) { - startX = 0; - startY = 0; goTurtle(); let scale = Math.min((width - drawingPadding) / (boundingBox.width), (height - drawingPadding) / (boundingBox.height)); canvasScaleX = canvasScaleY = scale; - canvasScrollX = (drawing_bounds.x * scale - width * .5); - canvasScrollY = (drawing_bounds.y * scale - height * .5); + canvasScrollX = (drawingBounds.x * scale - width * .5); + canvasScrollY = (drawingBounds.y * scale - height * .5); goTurtle(); } function afterCommandExecuted() { if (turtle.pen) { - drawing_bounds.includePoint(turtle.x, turtle.y); + drawingBounds.includePoint(turtle.x, turtle.y); } } function goTurtle() { - console.log({startX:startX,startY:startY}); - turtle = new Turtle(startX / canvasScaleX, startY / canvasScaleY, 0); + turtle = new Turtle(0, 0, 0); + drawingBounds.reset(); + drawingBounds.move(turtle.x, turtle.y); background(bgcolor); push(); translate(-canvasScrollX, -canvasScrollY); + scale(canvasScaleX, canvasScaleY); push(); - scale(canvasScaleX, canvasScaleY); turtle.reset(); let code = editor.value(); let parser = new Parser(code, afterCommandExecuted); @@ -111,8 +106,13 @@ function goTurtle() { for (let cmd of commands) { cmd.execute(); } + pop(); + noFill(); + stroke(255, 0, 0); + rect(drawingBounds.left, drawingBounds.top, drawingBounds.width, drawingBounds.height); + pop(); } @@ -136,13 +136,11 @@ function createTestDataView(cases) { turtle.y = height / 2; xOffset = 0; yOffset = 0; - startX = 100; - startY = 100; canvasScrollX = 0; canvasScrollY = 0; canvasScaleX = 1; canvasScaleY = 1; - + goTurtle(); return; } @@ -155,6 +153,6 @@ function createTestDataView(cases) { turtle.y = height / 2; canvasScrollX = canvasScrollY = 0; - scaleToFitBoundingBox(drawing_bounds); + scaleToFitBoundingBox(drawingBounds); }); } From a066e09b0dc1d76f32c4e7b5a5cf9fca5bc65bc6 Mon Sep 17 00:00:00 2001 From: TheTastefulToastie Date: Tue, 13 Nov 2018 00:28:08 +0000 Subject: [PATCH 2/5] Remove bounding box debug rect() --- sketch.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sketch.js b/sketch.js index cf62565..5cc298e 100644 --- a/sketch.js +++ b/sketch.js @@ -109,10 +109,6 @@ function goTurtle() { pop(); - noFill(); - stroke(255, 0, 0); - rect(drawingBounds.left, drawingBounds.top, drawingBounds.width, drawingBounds.height); - pop(); } From 7b1889a1a94d50469f110dd0b6279f6e3b2b572d Mon Sep 17 00:00:00 2001 From: TheTastefulToastie Date: Tue, 13 Nov 2018 02:18:30 +0000 Subject: [PATCH 3/5] Switch statements should have breaks, not bugs :ant: --- command.js | 19 ++++++------------- expression.js | 4 ++-- turtle.js | 2 +- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/command.js b/command.js index efd2171..51c4cea 100644 --- a/command.js +++ b/command.js @@ -40,24 +40,20 @@ class CommandArg { switch (type) { case ARGUMENT_TYPES.INT: this.validator = (arg) => { - if (arg instanceof Expression) - arg = arg.eval(); return /^\d+$/.test(arg); } break; case ARGUMENT_TYPES.FLOAT: this.validator = (arg) => { - if (arg instanceof Expression) - arg = arg.eval(); return /^[-+]?[0-9]*\.?[0-9]*$/.test(arg); } - + break; case ARGUMENT_TYPES.EXPRESSION: this.validator = (str) => { let res = /^[-+]?([0-9]+\.?[0-9]?|[0-9]*\.?[0-9]+)(\s[+-/*]{1}\s[-+]?([0-9]+\.?[0-9]?|[0-9]*\.?[0-9]+))*$/.test(str); return res; } - + break; } } else this.validator = validator; @@ -117,21 +113,19 @@ class CommandExecutor { this.values.push(value); break; case ARGUMENT_TYPES.INT: - this.values.push(value); + this.values.push(parseInt(value)); break; case ARGUMENT_TYPES.FLOAT: - this.values.push(value); + this.values.push(parseFloat(value)); break; case ARGUMENT_TYPES.COMMANDS: this.values.push( new Parser(value, this.callback).parse() ); break; - case ARGUMENT_TYPES.EXPRESSION: - //console.log(this.parseExpression(value)) - this.values.push(this.parseExpression(value).eval()) - + this.values.push(this.parseExpression(value).eval()); + break; case ARGUMENT_TYPES.PARAMETERS: // Example this.values.push(value.split(" ")); break; @@ -152,7 +146,6 @@ class CommandExecutor { e = new Expression(next,new Expression('$',token), right); } else return new Expression('$', token); - //console.log(right); if (right.lvl() > e.lvl()) { let new_left = new Expression(next, new Expression('$',token), right.left); e = new Expression(right.type, new_left, right.right); diff --git a/expression.js b/expression.js index ff79632..e4d2e12 100644 --- a/expression.js +++ b/expression.js @@ -7,8 +7,8 @@ class Expression { eval() { if(this.type == '$') { - return parseFloat(this.left); - } else if(this.type == '/') { + return parseFloat(this.left); + } else if(this.type == '/') { return this.left.eval() / this.right.eval(); } else if(this.type == '*') { return this.left.eval() * this.right.eval(); diff --git a/turtle.js b/turtle.js index 723d231..e826ce3 100644 --- a/turtle.js +++ b/turtle.js @@ -34,6 +34,6 @@ class Turtle { home() { this.x = this.homeX; - this.y = this.homey; + this.y = this.homeY; } } From 18499fd705d0d04e251eac14a89b1aaefb2d6ea5 Mon Sep 17 00:00:00 2001 From: TheTastefulToastie Date: Tue, 13 Nov 2018 03:34:08 +0000 Subject: [PATCH 4/5] Changed variable name back In a previous commit changed 'str' to 'arg' when arg could be a mix of int-as-string/float-as-string/Expression #55 Changed this and now it will always be string so 'str' is more appropriate. --- command.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/command.js b/command.js index 51c4cea..be8bf2e 100644 --- a/command.js +++ b/command.js @@ -39,13 +39,13 @@ class CommandArg { if (validator === undefined) { switch (type) { case ARGUMENT_TYPES.INT: - this.validator = (arg) => { - return /^\d+$/.test(arg); + this.validator = (str) => { + return /^\d+$/.test(str); } break; case ARGUMENT_TYPES.FLOAT: - this.validator = (arg) => { - return /^[-+]?[0-9]*\.?[0-9]*$/.test(arg); + this.validator = (str) => { + return /^[-+]?[0-9]*\.?[0-9]*$/.test(str); } break; case ARGUMENT_TYPES.EXPRESSION: From b6e9c6e31f3b23e7f361068ece9005a8d433d5b6 Mon Sep 17 00:00:00 2001 From: TheTastefulToastie Date: Tue, 13 Nov 2018 06:22:57 +0000 Subject: [PATCH 5/5] [Bugfix] Can't reselect default drawing Looks like selecting the 'Default' item in the dropdown used to clear the textarea and the drawing until [this](https://github.com/CodingTrain/Logo/pull/51/files#diff-9f2094443505273ff51ea1d1702e4367L101) commit removed it. So now the drawing remains unchanged when selecting the default item. It then resets the camera position and calls goTurtle() to redraw which causes the view to jump off-center. Instead it should put the default Logo in the textarea and call scaleToFitBoundingBox() to redraw _and_ center the drawing. This commit makes this change. --- index.html | 4 ---- sketch.js | 33 +++++++++++++++++++-------------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/index.html b/index.html index 0585fd8..caf21c9 100644 --- a/index.html +++ b/index.html @@ -46,10 +46,6 @@ Icons made by Turtle from www.flaticon.com is licensed by CC 3.0 BY - - diff --git a/sketch.js b/sketch.js index 98f5266..e7937df 100644 --- a/sketch.js +++ b/sketch.js @@ -64,6 +64,7 @@ function setup() { } editor = select("#code"); + setDefaultDrawing(); editor.input(goTurtle); scaleToFitBoundingBox(drawingBounds); // This also redraws (it has to in order to measure the size of the drawing) } @@ -109,9 +110,17 @@ function goTurtle() { pop(); } +/** + * Writes the Logo code for the default drawing to the textarea + * Called on page load + * Also called when selecting the default item from the #testdata dropdown + */ +function setDefaultDrawing() { + editor.value("pu lt 90 fd 100 lt 90 fd 250 rt 90 rt 90 pd fd 500 rt 90 fd 150 rt 90 fd 500 rt 90 fd 150"); +} + function createTestDataView(cases) { let selector = select("#testdata"); - allCases = cases; selector.option("Logo Default", -1); @@ -123,30 +132,26 @@ function createTestDataView(cases) { selector.changed(function() { let val = parseInt(selector.value()); if (val < 0) { - turtle.strokeColor = 255; - turtle.dir = 0; - turtle.x = 0; - turtle.y = 0; - canvasScrollX = 0; - canvasScrollY = 0; - canvasScaleX = 1; - canvasScaleY = 1; - - goTurtle(); - return; + // Use the default drawing + setDefaultDrawing(); + } else { + // Use a drawing from tests.json + editor.value(cases[val].code); } - editor.value(allCases[val].code); - + // Reset default parameters for turtle turtle.strokeColor = 255; turtle.dir = 0; turtle.x = 0; turtle.y = 0; + // Reset default parameters for camera canvasScrollX = 0; canvasScrollY = 0; canvasScaleX = 1; canvasScaleY = 1; + + // Move and scale the drawing to fit on-screen scaleToFitBoundingBox(drawingBounds); }); }