From 24be421d5e5c022c86fdee4f18156eca95e2e1f1 Mon Sep 17 00:00:00 2001 From: Lewis Hemens Date: Sat, 18 Jan 2014 00:28:46 +0000 Subject: [PATCH] Partial completion of basic move functionality, missing deletion of existing structures however --- js-lib/common.js | 7 ++ js-lib/state-controller.js | 182 +++++++++++++++++++++++-------------- js-lib/state.js | 44 ++++++--- root.html | 1 + 4 files changed, 157 insertions(+), 77 deletions(-) diff --git a/js-lib/common.js b/js-lib/common.js index ef402e7..3dc527c 100644 --- a/js-lib/common.js +++ b/js-lib/common.js @@ -37,6 +37,13 @@ ascii.Vector.prototype.add = function(other) { return new ascii.Vector(this.x + other.x, this.y + other.y); }; +/** + * @return {ascii.Vector} + */ +ascii.Vector.prototype.clone = function() { + return new ascii.Vector(this.x, this.y); +}; + /** @return {number} */ ascii.Vector.prototype.length = function() { return Math.sqrt(this.x * this.x + this.y * this.y); diff --git a/js-lib/state-controller.js b/js-lib/state-controller.js index 803e6cd..adfd2ea 100644 --- a/js-lib/state-controller.js +++ b/js-lib/state-controller.js @@ -2,6 +2,29 @@ goog.provide('ascii.StateController'); goog.require('ascii.Vector'); +/** + * Draws a line. + */ +function drawLine(state, startPosition, endPosition, clockwise) { + var hX1 = Math.min(startPosition.x, endPosition.x); + var vY1 = Math.min(startPosition.y, endPosition.y); + var hX2 = Math.max(startPosition.x, endPosition.x); + var vY2 = Math.max(startPosition.y, endPosition.y); + + var hY = clockwise ? startPosition.y : endPosition.y; + var vX = clockwise ? endPosition.x : startPosition.x; + + while (hX1++ < hX2) { + state.drawSpecial(new ascii.Vector(hX1, hY)); + } + while (vY1++ < vY2) { + state.drawSpecial(new ascii.Vector(vX, vY1)); + } + state.drawSpecial(startPosition); + state.drawSpecial(endPosition); + state.drawSpecial(new ascii.Vector(vX, hY)); +}; + /** * Common interface for different drawing functions, e.g. box, line, etc. * @interface @@ -22,45 +45,21 @@ DrawFunction.prototype.end = function(position) {}; function DrawBox(state) { this.state = state; /** @type {ascii.Vector} */ this.startPosition = null; - /** @type {ascii.Vector} */ this.endPosition = null; } DrawBox.prototype.start = function(position) { this.startPosition = position; - this.endPosition = position; - this.draw(); }; DrawBox.prototype.move = function(position) { this.endPosition = position; this.state.clearDraw(); - this.draw(); + drawLine(this.state, this.startPosition, position, true); + drawLine(this.state, this.startPosition, position, false); }; DrawBox.prototype.end = function(position) { this.state.commitDraw(); }; -/** Draws the currently dragged out box. */ -DrawBox.prototype.draw = function() { - var x1 = Math.min(this.startPosition.x, this.endPosition.x); - var y1 = Math.min(this.startPosition.y, this.endPosition.y); - var x2 = Math.max(this.startPosition.x, this.endPosition.x); - var y2 = Math.max(this.startPosition.y, this.endPosition.y); - - this.state.drawSpecial(new ascii.Vector(x1, y1)); - this.state.drawSpecial(new ascii.Vector(x1, y2)); - this.state.drawSpecial(new ascii.Vector(x2, y1)); - this.state.drawSpecial(new ascii.Vector(x2, y2)); - - - for (var x = x1 + 1; x < x2; x++) { - this.state.drawSpecial(new ascii.Vector(x, y1)); - this.state.drawSpecial(new ascii.Vector(x, y2)); - } - for (var y = y1 + 1; y < y2; y++) { - this.state.drawSpecial(new ascii.Vector(x1, y)); - this.state.drawSpecial(new ascii.Vector(x2, y)); - } -}; /** * @constructor @@ -70,58 +69,27 @@ DrawBox.prototype.draw = function() { function DrawLine(state) { this.state = state; /** @type {ascii.Vector} */ this.startPosition = null; - /** @type {ascii.Vector} */ this.endPosition = null; } DrawLine.prototype.start = function(position) { this.startPosition = position; - this.endPosition = position; - this.draw(); }; DrawLine.prototype.move = function(position) { - this.endPosition = position; this.state.clearDraw(); - this.draw(); + + // Try to infer line orientation. + var startContext = this.state.getContext(this.startPosition); + var endContext = this.state.getContext(position); + var clockwise = (startContext.up && startContext.down) || + (endContext.left && endContext.right); + + drawLine(this.state, this.startPosition, position, clockwise); }; + DrawLine.prototype.end = function(position) { this.state.commitDraw(); }; -/** - * Draws the currently dragged out line. - */ -DrawLine.prototype.draw = function() { - var upStart = this.state.isSpecial( - this.startPosition.add(new ascii.Vector(0, -1))); - var downStart = this.state.isSpecial( - this.startPosition.add(new ascii.Vector(0, 1))); - var leftEnd = this.state.isSpecial( - this.endPosition.add(new ascii.Vector(-1, 0))); - var rightEnd = this.state.isSpecial( - this.endPosition.add(new ascii.Vector(1, 0))); - - // Look at the start and end contexts to infer line orientation. - var isClockwise = (upStart && downStart) || (leftEnd && rightEnd); - - var hX1 = Math.min(this.startPosition.x, this.endPosition.x); - var vY1 = Math.min(this.startPosition.y, this.endPosition.y); - var hX2 = Math.max(this.startPosition.x, this.endPosition.x); - var vY2 = Math.max(this.startPosition.y, this.endPosition.y); - - var hY = isClockwise ? this.startPosition.y : this.endPosition.y; - var vX = isClockwise ? this.endPosition.x : this.startPosition.x; - - while (hX1++ < hX2) { - this.state.drawSpecial(new ascii.Vector(hX1, hY)); - } - while (vY1++ < vY2) { - this.state.drawSpecial(new ascii.Vector(vX, vY1)); - } - this.state.drawSpecial(new ascii.Vector(this.startPosition.x, this.startPosition.y)); - this.state.drawSpecial(new ascii.Vector(this.endPosition.x, this.endPosition.y)); - this.state.drawSpecial(new ascii.Vector(vX, hY)); -}; - /** * @constructor * @implements {DrawFunction} @@ -143,6 +111,84 @@ DrawFreeform.prototype.move = function(position) { DrawFreeform.prototype.end = function(position) { }; +/** + * @constructor + * @implements {DrawFunction} + * @param {ascii.State} state + */ +function DrawMove(state) { + this.state = state; + this.ends = null; +} + +DrawMove.prototype.start = function(position) { + var context = this.state.getContext(position); + var directions = [ + new ascii.Vector(1, 0), + new ascii.Vector(-1, 0), + new ascii.Vector(0, 1), + new ascii.Vector(0, -1) ]; + + var ends = []; + for (var i in directions) { + var midPoint = this.followLine(position, directions[i]); + // Clockwise is a lie, it is true if we move vertically first. + var clockwise = (directions[i].x != 0); + // Ignore any directions that didn't go anywhere. + if (position.equals(midPoint)) { + continue; + } + var midPointContext = this.state.getContext(midPoint); + // Special case, a straight line with no turns. + if ((midPointContext.left + midPointContext.right + + midPointContext.up + midPointContext.down) == 1) { + ends.push({position: midPoint, clockwise: clockwise}); + continue; + } + // Continue following lines from the midpoint. + for (var j in directions) { + if (directions[i].add(directions[j]).length() == 0) { + // Don't go back on ourselves. + continue; + } + var end = this.followLine(midPoint, directions[j]); + // Ignore any directions that didn't go anywhere. + if (midPoint.equals(end)) { + continue; + } + ends.push({position: end, clockwise: clockwise}); + } + } + this.ends = ends; +}; + +DrawMove.prototype.move = function(position) { + this.state.clearDraw(); + for (var i in this.ends) { + drawLine(this.state, position, this.ends[i].position, this.ends[i].clockwise); + } +}; + +DrawMove.prototype.end = function(position) { + this.state.commitDraw(); +}; + +DrawMove.prototype.followLine = function(startPosition, direction) { + var endPosition = startPosition.clone(); + while (true) { + var nextEnd = endPosition.add(direction); + if (!this.state.isSpecial(nextEnd)) { + return endPosition; + } + endPosition = nextEnd; + var context = this.state.getContext(nextEnd); + if (!(context.left && context.right && !context.up && !context.down) && + !(!context.left && !context.right && context.up && context.down)) { + return endPosition; + } + } +}; + /** * Handles management of the diagram state. Input events are cleaned in the * parent controller and passed down to this class for dealing with drawing. @@ -169,6 +215,10 @@ ascii.StateController = function(state) { $('#erase-button').click(function(e) { this.drawFunction = new DrawFreeform(state, null); }.bind(this)); + + $('#move-button').click(function(e) { + this.drawFunction = new DrawMove(state); + }.bind(this)); }; /** diff --git a/js-lib/state.js b/js-lib/state.js index 58504ee..f6bfdbd 100644 --- a/js-lib/state.js +++ b/js-lib/state.js @@ -21,6 +21,19 @@ ascii.Cell.prototype.getDrawValue = function() { return (this.scratchValue != null ? this.scratchValue : this.value); }; +/** + * The context for a cell, i.e. the status of the cells around it. + * + * @param {boolean} left, right, up, down + * @constructor + */ +ascii.CellContext = function(left, right, up, down) { + /** @type {boolean} */ this.left = left; + /** @type {boolean} */ this.right = right; + /** @type {boolean} */ this.up = up; + /** @type {boolean} */ this.down = down; +}; + /** * Holds the entire state of the diagram as a 2D array of cells. * @@ -118,21 +131,30 @@ ascii.State.prototype.getDrawValue = function(position) { } // Magic time. + var context = this.getContext(position); + + if (context.left && context.right && !context.up && !context.down) { + return SPECIAL_LINE_H; + } + if (!context.left && !context.right && context.up && context.down) { + return SPECIAL_LINE_V; + } + if (context.left && context.right && context.up && context.down) { + return SPECIAL_LINE_H; + } + return SPECIAL_VALUE; +}; + +/** + * @param {ascii.Vector} position + * @return {ascii.CellContext} + */ +ascii.State.prototype.getContext = function(position) { var left = this.isSpecial(position.add(new ascii.Vector(-1, 0))); var right = this.isSpecial(position.add(new ascii.Vector(1, 0))); var up = this.isSpecial(position.add(new ascii.Vector(0, -1))); var down = this.isSpecial(position.add(new ascii.Vector(0, 1))); - - if (left && right && !up && !down) { - return SPECIAL_LINE_H; - } - if (!left && !right && up && down) { - return SPECIAL_LINE_V; - } - if (left && right && up && down) { - return SPECIAL_LINE_H; - } - return SPECIAL_VALUE; + return new ascii.CellContext(left, right, up, down); }; /** diff --git a/root.html b/root.html index 83880ce..a971f8d 100644 --- a/root.html +++ b/root.html @@ -39,6 +39,7 @@ button { +