Lots of code tidying

This commit is contained in:
Lewis Hemens 2014-01-19 14:23:17 +00:00
parent fc59101bf5
commit 7aca5737a4
7 changed files with 156 additions and 207 deletions

View File

@ -1,7 +1,7 @@
java -client -jar closure-compiler.jar \ java -client -jar closure-compiler.jar \
--js js-lib/common.js \ --js js-lib/common.js \
--js js-lib/view.js \ --js js-lib/view.js \
--js js-lib/state-controller.js \ --js js-lib/draw.js \
--js js-lib/controller.js \ --js js-lib/controller.js \
--js js-lib/state.js \ --js js-lib/state.js \
--js js-lib/launch.js \ --js js-lib/launch.js \

View File

@ -1,8 +1,24 @@
/** /**
* Common classes. * Common classes and constants.
*/ */
goog.provide('ascii');
/** @const */ var MAX_GRID_SIZE = 1000;
/** @const */ var SPECIAL_VALUE = '+';
/** @const */ var SPECIAL_LINE_H = '\u2014';
/** @const */ var SPECIAL_LINE_V = '|';
/** @const */ var DRAG_LATENCY = 130; // Milliseconds.
/** @const */ var DRAG_ACCURACY = 3; // Pixels.
/** @const */ var CHARACTER_PIXELS = 15;
/** @const */ var RENDER_PADDING = 70;
/** /**
* Stores a 2D vector.
*
* @constructor * @constructor
* @param {number} x * @param {number} x
* @param {number} y * @param {number} y
@ -56,3 +72,46 @@ ascii.Vector.prototype.scale = function(scale) {
return new ascii.Vector(this.x * scale, this.y * scale); return new ascii.Vector(this.x * scale, this.y * scale);
}; };
/**
* An individual cell within the diagram and it's current value.
*
* @constructor
*/
ascii.Cell = function() {
/** @type {?string} */ this.value = null;
/** @type {?string} */ this.scratchValue = null;
};
/** @return {?string} */
ascii.Cell.prototype.getRawValue = function() {
return (this.scratchValue != null ? this.scratchValue : this.value);
};
/** @return {boolean} */
ascii.Cell.prototype.isSpecial = function() {
return this.getRawValue() == SPECIAL_VALUE;
};
/**
* The context for a cell, i.e. the status of the cells around it.
*
* @param {boolean} left
* @param {boolean} right
* @param {boolean} up
* @param {boolean} 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;
};
/**
* Returns the total number of surrounding special cells.
* @return {number}
*/
ascii.CellContext.prototype.sum = function() {
return this.left + this.right + this.up + this.down;
};

View File

@ -1,11 +1,6 @@
/** /**
* Handles user input events and modifies state. * Handles user input events and modifies state.
*/ *
/** @const */ var DRAG_LATENCY = 130; // Milliseconds.
/** @const */ var DRAG_ACCURACY = 3; // Pixels.
/**
* @constructor * @constructor
* @param {ascii.View} view * @param {ascii.View} view
* @param {ascii.State} state * @param {ascii.State} state
@ -14,8 +9,8 @@ ascii.Controller = function(view, state) {
/** @type {ascii.View} */ this.view = view; /** @type {ascii.View} */ this.view = view;
/** @type {ascii.State} */ this.state = state; /** @type {ascii.State} */ this.state = state;
/** @type {ascii.StateController} */ this.stateController = /** @type {ascii.DrawFunction} */ this.drawFunction =
new ascii.StateController(state); new ascii.DrawBox(state);
/** @type {ascii.Vector} */ this.dragOrigin; /** @type {ascii.Vector} */ this.dragOrigin;
/** @type {ascii.Vector} */ this.pressVector; /** @type {ascii.Vector} */ this.pressVector;
@ -35,7 +30,7 @@ ascii.Controller.prototype.handlePress = function(position) {
// Check to see if a drag happened in the given allowed time. // Check to see if a drag happened in the given allowed time.
window.setTimeout(function() { window.setTimeout(function() {
if (this.dragOrigin == null && this.pressVector != null) { if (this.dragOrigin == null && this.pressVector != null) {
this.stateController.handleDrawingPress(this.view.screenToCell(position)); this.drawFunction.start(this.view.screenToCell(position));
this.view.dirty = true; this.view.dirty = true;
} }
// TODO: Skip this if release happens before timeout. // TODO: Skip this if release happens before timeout.
@ -62,7 +57,7 @@ ascii.Controller.prototype.handleMove = function(position) {
(this.lastMoveCell == null || (this.lastMoveCell == null ||
!this.view.screenToCell(position) !this.view.screenToCell(position)
.equals(this.view.screenToCell(this.lastMoveCell)))) { .equals(this.view.screenToCell(this.lastMoveCell)))) {
this.stateController.handleDrawingMove(this.view.screenToCell(position)); this.drawFunction.move(this.view.screenToCell(position));
this.view.dirty = true; this.view.dirty = true;
this.lastMoveCell = position; this.lastMoveCell = position;
} }
@ -83,7 +78,7 @@ ascii.Controller.prototype.handleRelease = function(position) {
// Drag wasn't initiated in time, treat this as a drawing event. // Drag wasn't initiated in time, treat this as a drawing event.
if (this.dragOrigin == null && if (this.dragOrigin == null &&
($.now() - this.pressTimestamp) >= DRAG_LATENCY) { ($.now() - this.pressTimestamp) >= DRAG_LATENCY) {
this.stateController.handleDrawingRelease(this.view.screenToCell(position)); this.drawFunction.end(this.view.screenToCell(position));
this.view.dirty = true; this.view.dirty = true;
} }
this.pressVector = null; this.pressVector = null;
@ -150,4 +145,24 @@ ascii.Controller.prototype.installBindings = function() {
e.originalEvent.touches[0].pageY)); e.originalEvent.touches[0].pageY));
}); });
// TODO: Handle pinch to zoom. // TODO: Handle pinch to zoom.
$('#box-button').click(function(e) {
this.drawFunction = new ascii.DrawBox(this.state);
}.bind(this));
$('#line-button').click(function(e) {
this.drawFunction = new ascii.DrawLine(this.state);
}.bind(this));
$('#freeform-button').click(function(e) {
this.drawFunction = new ascii.DrawFreeform(this.state, '+');
}.bind(this));
$('#erase-button').click(function(e) {
this.drawFunction = new ascii.DrawFreeform(this.state, null);
}.bind(this));
$('#move-button').click(function(e) {
this.drawFunction = new ascii.DrawMove(this.state);
}.bind(this));
}; };

View File

@ -1,7 +1,19 @@
/** /**
* Draws a line. * All drawing classes and functions.
*/ */
function drawLine(state, startPosition, endPosition, clockwise) {
/**
* Draws a line on the diagram state.
*
* @param {ascii.State} state
* @param {ascii.Vector} startPosition
* @param {ascii.Vector} endPosition
* @param {boolean} clockwise
* @param {string=} opt_value
*/
function drawLine(state, startPosition, endPosition, clockwise, opt_value) {
var value = opt_value || SPECIAL_VALUE;
var hX1 = Math.min(startPosition.x, endPosition.x); var hX1 = Math.min(startPosition.x, endPosition.x);
var vY1 = Math.min(startPosition.y, endPosition.y); var vY1 = Math.min(startPosition.y, endPosition.y);
var hX2 = Math.max(startPosition.x, endPosition.x); var hX2 = Math.max(startPosition.x, endPosition.x);
@ -11,93 +23,70 @@ function drawLine(state, startPosition, endPosition, clockwise) {
var vX = clockwise ? endPosition.x : startPosition.x; var vX = clockwise ? endPosition.x : startPosition.x;
while (hX1++ < hX2) { while (hX1++ < hX2) {
state.drawSpecial(new ascii.Vector(hX1, hY)); state.drawValue(new ascii.Vector(hX1, hY), value);
} }
while (vY1++ < vY2) { while (vY1++ < vY2) {
state.drawSpecial(new ascii.Vector(vX, vY1)); state.drawValue(new ascii.Vector(vX, vY1), value);
} }
state.drawSpecial(startPosition); state.drawValue(startPosition, value);
state.drawSpecial(endPosition); state.drawValue(endPosition, value);
state.drawSpecial(new ascii.Vector(vX, hY)); state.drawValue(new ascii.Vector(vX, hY), value);
}; }
/**
* Clears a line but with some special cases.
* TODO: Refactor somewhere!
*/
function clearLine(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.drawValue(new ascii.Vector(hX1, hY), ' ');
}
while (vY1++ < vY2) {
state.drawValue(new ascii.Vector(vX, vY1), ' ');
}
state.drawValue(startPosition, ' ');
state.drawValue(endPosition, ' ');
state.drawValue(new ascii.Vector(vX, hY), ' ');
};
/** /**
* Common interface for different drawing functions, e.g. box, line, etc. * Common interface for different drawing functions, e.g. box, line, etc.
* @interface * @interface
*/ */
function DrawFunction() {} ascii.DrawFunction = function() {};
/** Start of drawing. @param {ascii.Vector} position */ /** Start of drawing. @param {ascii.Vector} position */
DrawFunction.prototype.start = function(position) {}; ascii.DrawFunction.prototype.start = function(position) {};
/** Drawing move. @param {ascii.Vector} position */ /** Drawing move. @param {ascii.Vector} position */
DrawFunction.prototype.move = function(position) {}; ascii.DrawFunction.prototype.move = function(position) {};
/** End of drawing. @param {ascii.Vector} position */ /** End of drawing. @param {ascii.Vector} position */
DrawFunction.prototype.end = function(position) {}; ascii.DrawFunction.prototype.end = function(position) {};
/** /**
* @constructor * @constructor
* @implements {DrawFunction} * @implements {ascii.DrawFunction}
* @param {ascii.State} state * @param {ascii.State} state
*/ */
function DrawBox(state) { ascii.DrawBox = function(state) {
this.state = state; this.state = state;
/** @type {ascii.Vector} */ this.startPosition = null; /** @type {ascii.Vector} */ this.startPosition = null;
} };
DrawBox.prototype.start = function(position) { ascii.DrawBox.prototype.start = function(position) {
this.startPosition = position; this.startPosition = position;
}; };
DrawBox.prototype.move = function(position) { ascii.DrawBox.prototype.move = function(position) {
this.endPosition = position; this.endPosition = position;
this.state.clearDraw(); this.state.clearDraw();
drawLine(this.state, this.startPosition, position, true); drawLine(this.state, this.startPosition, position, true);
drawLine(this.state, this.startPosition, position, false); drawLine(this.state, this.startPosition, position, false);
}; };
DrawBox.prototype.end = function(position) { ascii.DrawBox.prototype.end = function(position) {
this.state.commitDraw(); this.state.commitDraw();
}; };
/** /**
* @constructor * @constructor
* @implements {DrawFunction} * @implements {ascii.DrawFunction}
* @param {ascii.State} state * @param {ascii.State} state
*/ */
function DrawLine(state) { ascii.DrawLine = function(state) {
this.state = state; this.state = state;
/** @type {ascii.Vector} */ this.startPosition = null; /** @type {ascii.Vector} */ this.startPosition = null;
} };
DrawLine.prototype.start = function(position) { ascii.DrawLine.prototype.start = function(position) {
this.startPosition = position; this.startPosition = position;
}; };
DrawLine.prototype.move = function(position) { ascii.DrawLine.prototype.move = function(position) {
this.state.clearDraw(); this.state.clearDraw();
// Try to infer line orientation. // Try to infer line orientation.
// TODO: Split the line into two lines if we can't satisfy both ends.
var startContext = this.state.getContext(this.startPosition); var startContext = this.state.getContext(this.startPosition);
var endContext = this.state.getContext(position); var endContext = this.state.getContext(position);
var clockwise = (startContext.up && startContext.down) || var clockwise = (startContext.up && startContext.down) ||
@ -106,48 +95,48 @@ DrawLine.prototype.move = function(position) {
drawLine(this.state, this.startPosition, position, clockwise); drawLine(this.state, this.startPosition, position, clockwise);
}; };
DrawLine.prototype.end = function(position) { ascii.DrawLine.prototype.end = function(position) {
this.state.commitDraw(); this.state.commitDraw();
}; };
/** /**
* @constructor * @constructor
* @implements {DrawFunction} * @implements {ascii.DrawFunction}
* @param {ascii.State} state * @param {ascii.State} state
* @param {?string} value * @param {?string} value
*/ */
function DrawFreeform(state, value) { ascii.DrawFreeform = function(state, value) {
this.state = state; this.state = state;
this.value = value; this.value = value;
} };
DrawFreeform.prototype.start = function(position) { ascii.DrawFreeform.prototype.start = function(position) {
this.state.setValue(position, this.value); this.state.setValue(position, this.value);
}; };
DrawFreeform.prototype.move = function(position) { ascii.DrawFreeform.prototype.move = function(position) {
this.state.setValue(position, this.value); this.state.setValue(position, this.value);
}; };
DrawFreeform.prototype.end = function(position) { ascii.DrawFreeform.prototype.end = function(position) {
}; };
/** /**
* @constructor * @constructor
* @implements {DrawFunction} * @implements {ascii.DrawFunction}
* @param {ascii.State} state * @param {ascii.State} state
*/ */
function DrawMove(state) { ascii.DrawMove = function(state) {
this.state = state; this.state = state;
this.ends = null; this.ends = null;
} };
DrawMove.prototype.start = function(position) { ascii.DrawMove.prototype.start = function(position) {
var context = this.state.getContext(position); var context = this.state.getContext(position);
var directions = [ var directions = [
new ascii.Vector(1, 0), new ascii.Vector(1, 0),
new ascii.Vector(-1, 0), new ascii.Vector(-1, 0),
new ascii.Vector(0, 1), new ascii.Vector(0, 1),
new ascii.Vector(0, -1) ]; new ascii.Vector(0, -1)];
var ends = []; var ends = [];
for (var i in directions) { for (var i in directions) {
@ -175,7 +164,7 @@ DrawMove.prototype.start = function(position) {
// Don't go back on ourselves, or don't carry on in same direction. // Don't go back on ourselves, or don't carry on in same direction.
continue; continue;
} }
// On the second line we don't care about multiple junctions, just the first. // On the second line we don't care about multiple junctions.
var endz = this.followLine(midPoint, directions[j]); var endz = this.followLine(midPoint, directions[j]);
// Ignore any directions that didn't go anywhere. // Ignore any directions that didn't go anywhere.
if (endz.length == 0 || midPoint.equals(endz[0])) { if (endz.length == 0 || midPoint.equals(endz[0])) {
@ -189,30 +178,32 @@ DrawMove.prototype.start = function(position) {
// Clear all the lines so we can draw them afresh. // Clear all the lines so we can draw them afresh.
for (var i in ends) { for (var i in ends) {
clearLine(this.state, position, ends[i].position, ends[i].clockwise); drawLine(this.state, position, ends[i].position, ends[i].clockwise, ' ');
} }
this.state.commitDraw(); this.state.commitDraw();
// Redraw the new lines after we have cleared the existing ones. // Redraw the new lines after we have cleared the existing ones.
this.move(position); this.move(position);
}; };
DrawMove.prototype.move = function(position) { ascii.DrawMove.prototype.move = function(position) {
this.state.clearDraw(); this.state.clearDraw();
for (var i in this.ends) { for (var i in this.ends) {
drawLine(this.state, position, this.ends[i].position, this.ends[i].clockwise); drawLine(this.state, position, this.ends[i].position,
this.ends[i].clockwise);
} }
}; };
DrawMove.prototype.end = function(position) { ascii.DrawMove.prototype.end = function(position) {
this.state.commitDraw(); this.state.commitDraw();
}; };
DrawMove.prototype.followLine = function(startPosition, direction) { ascii.DrawMove.prototype.followLine = function(startPosition, direction) {
var endPosition = startPosition.clone(); var endPosition = startPosition.clone();
var junctions = []; var junctions = [];
while (true) { while (true) {
var nextEnd = endPosition.add(direction); var nextEnd = endPosition.add(direction);
if (!this.state.isSpecial(endPosition) || !this.state.isSpecial(nextEnd)) { if (!this.state.getCell(endPosition).isSpecial() ||
!this.state.getCell(nextEnd).isSpecial()) {
return junctions; return junctions;
} }
endPosition = nextEnd; endPosition = nextEnd;
@ -224,58 +215,3 @@ DrawMove.prototype.followLine = function(startPosition, direction) {
} }
}; };
/**
* Handles management of the diagram state. Input events are cleaned in the
* parent controller and passed down to this class for dealing with drawing.
*
* @constructor
* @param {ascii.State} state
*/
ascii.StateController = function(state) {
/** @type {ascii.State} */ this.state = state;
/** @type {DrawFunction} */ this.drawFunction = new DrawBox(state);
$('#box-button').click(function(e) {
this.drawFunction = new DrawBox(state);
}.bind(this));
$('#line-button').click(function(e) {
this.drawFunction = new DrawLine(state);
}.bind(this));
$('#freeform-button').click(function(e) {
this.drawFunction = new DrawFreeform(state, '+');
}.bind(this));
$('#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));
};
/**
* Handles a press in the context of the drawing frame.
* @param {ascii.Vector} position
*/
ascii.StateController.prototype.handleDrawingPress = function(position) {
this.drawFunction.start(position);
};
/**
* Handles a release in the context of the drawing frame.
* @param {ascii.Vector} position
*/
ascii.StateController.prototype.handleDrawingRelease = function(position) {
this.drawFunction.end(position);
};
/**
* Handles a move in the context of the drawing frame.
* @param {ascii.Vector} position
*/
ascii.StateController.prototype.handleDrawingMove = function(position) {
this.drawFunction.move(position);
};

View File

@ -1,7 +1,3 @@
/**
* Application main entry point.
*/
/** /**
* Runs the application. * Runs the application.
*/ */

View File

@ -1,39 +1,6 @@
/** @const */ var MAX_GRID_SIZE = 1000;
/** @const */ var SPECIAL_VALUE = '+';
/** @const */ var SPECIAL_LINE_H = '\u2014';
/** @const */ var SPECIAL_LINE_V = '|';
/** /**
* An individual cell within the diagram and it's current value. * Holds the entire state of the diagram as a 2D array of cells
* * and provides methods to modify the current state.
* @constructor
*/
ascii.Cell = function() {
/** @type {?string} */ this.value = null;
/** @type {?string} */ this.scratchValue = null;
};
/** @return {?string} */
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.
* *
* @constructor * @constructor
*/ */
@ -71,7 +38,7 @@ ascii.State.prototype.getCell = function(vector) {
ascii.State.prototype.setValue = function(position, value) { ascii.State.prototype.setValue = function(position, value) {
this.getCell(position).value = value; this.getCell(position).value = value;
}; };
/** /**
* Sets the cells scratch (uncommitted) value at the given position. * Sets the cells scratch (uncommitted) value at the given position.
* *
@ -84,15 +51,6 @@ ascii.State.prototype.drawValue = function(position, value) {
cell.scratchValue = value; cell.scratchValue = value;
}; };
/**
* Sets the cells scratch value to be the special value.
*
* @param {ascii.Vector} position
*/
ascii.State.prototype.drawSpecial = function(position) {
this.drawValue(position, SPECIAL_VALUE);
};
/** /**
* Clears the current drawing scratchpad. * Clears the current drawing scratchpad.
*/ */
@ -103,18 +61,6 @@ ascii.State.prototype.clearDraw = function() {
this.scratchCells.length = 0; this.scratchCells.length = 0;
}; };
/**
* Returns true if the cell at the given position is special.
*
* @param {ascii.Vector} position
* @return {boolean}
*/
ascii.State.prototype.isSpecial = function(position) {
var cell = this.getCell(position);
var value = cell.scratchValue != null ? cell.scratchValue : cell.value;
return value == SPECIAL_VALUE;
};
/** /**
* Returns the draw value of a cell at the given position. * Returns the draw value of a cell at the given position.
* *
@ -123,14 +69,14 @@ ascii.State.prototype.isSpecial = function(position) {
*/ */
ascii.State.prototype.getDrawValue = function(position) { ascii.State.prototype.getDrawValue = function(position) {
var cell = this.getCell(position); var cell = this.getCell(position);
var value = cell.scratchValue != null ? cell.scratchValue : cell.value; var value = cell.scratchValue != null ? cell.scratchValue : cell.value;
if (value != SPECIAL_VALUE) { if (value != SPECIAL_VALUE) {
return value; return value;
} }
// Magic time. // Magic time.
var context = this.getContext(position); var context = this.getContext(position);
if (context.left && context.right && !context.up && !context.down) { if (context.left && context.right && !context.up && !context.down) {
return SPECIAL_LINE_H; return SPECIAL_LINE_H;
} }
@ -148,10 +94,10 @@ ascii.State.prototype.getDrawValue = function(position) {
* @return {ascii.CellContext} * @return {ascii.CellContext}
*/ */
ascii.State.prototype.getContext = function(position) { ascii.State.prototype.getContext = function(position) {
var left = this.isSpecial(position.add(new ascii.Vector(-1, 0))); var left = this.getCell(position.add(new ascii.Vector(-1, 0))).isSpecial();
var right = this.isSpecial(position.add(new ascii.Vector(1, 0))); var right = this.getCell(position.add(new ascii.Vector(1, 0))).isSpecial();
var up = this.isSpecial(position.add(new ascii.Vector(0, -1))); var up = this.getCell(position.add(new ascii.Vector(0, -1))).isSpecial();
var down = this.isSpecial(position.add(new ascii.Vector(0, 1))); var down = this.getCell(position.add(new ascii.Vector(0, 1))).isSpecial();
return new ascii.CellContext(left, right, up, down); return new ascii.CellContext(left, right, up, down);
}; };
@ -160,7 +106,7 @@ ascii.State.prototype.getContext = function(position) {
*/ */
ascii.State.prototype.commitDraw = function() { ascii.State.prototype.commitDraw = function() {
for (var i in this.scratchCells) { for (var i in this.scratchCells) {
this.scratchCells[i].value = this.scratchCells[i].getDrawValue(); this.scratchCells[i].value = this.scratchCells[i].getRawValue();
this.scratchCells[i].scratchValue = null; this.scratchCells[i].scratchValue = null;
} }
}; };

View File

@ -1,6 +1,3 @@
/** @const */ var CHARACTER_PIXELS = 15;
/** @const */ var RENDER_PADDING = 70;
/** /**
* Handles view operations, state and management of the screen. * Handles view operations, state and management of the screen.
* *
@ -92,7 +89,7 @@ ascii.View.prototype.render = function() {
this.context.font = '15px Courier New'; this.context.font = '15px Courier New';
for (var i = startOffset.x; i < endOffset.x; i++) { for (var i = startOffset.x; i < endOffset.x; i++) {
for (var j = startOffset.y; j < endOffset.y; j++) { for (var j = startOffset.y; j < endOffset.y; j++) {
if (this.state.isSpecial(new ascii.Vector(i, j))) { if (this.state.getCell(new ascii.Vector(i, j)).isSpecial()) {
this.context.fillStyle = '#F5F5F5'; this.context.fillStyle = '#F5F5F5';
context.fillRect( context.fillRect(
i * CHARACTER_PIXELS - this.offset.x, i * CHARACTER_PIXELS - this.offset.x,