Added box drawing and made this the default action

This commit is contained in:
Lewis Hemens 2014-01-12 16:51:16 +00:00
parent d2c6e799a8
commit dda36a9a7f
4 changed files with 139 additions and 10 deletions

View File

@ -42,6 +42,7 @@ ascii.Controller.prototype.handlePress = function(position) {
window.setTimeout(function() {
if (this.dragOrigin == null) {
this.stateController.handleDrawingPress(this.view.screenToCell(position));
this.view.dirty = true;
}
// TODO: Skip this if release happens before timeout.
}.bind(this), DRAG_LATENCY);
@ -68,6 +69,7 @@ ascii.Controller.prototype.handleMove = function(position) {
!this.view.screenToCell(position)
.equals(this.view.screenToCell(this.lastMoveCell)))) {
this.stateController.handleDrawingMove(this.view.screenToCell(position));
this.view.dirty = true;
this.lastMoveCell = position;
}
@ -88,6 +90,7 @@ ascii.Controller.prototype.handleRelease = function(position) {
if (this.dragOrigin == null &&
($.now() - this.pressTimestamp) >= DRAG_LATENCY) {
this.stateController.handleDrawingRelease(this.view.screenToCell(position));
this.view.dirty = true;
}
this.pressVector = null;
this.pressTimestamp = 0;
@ -118,6 +121,9 @@ ascii.Controller.prototype.installDesktopBindings = function() {
$(this.view.canvas).mouseup(function(e) {
controller.handleRelease(new ascii.Vector(e.clientX, e.clientY));
});
$(this.view.canvas).mouseleave(function(e) {
controller.handleRelease(new ascii.Vector(e.clientX, e.clientY));
});
$(this.view.canvas).mousemove(function(e) {
controller.handleMove(new ascii.Vector(e.clientX, e.clientY));
});

View File

@ -2,6 +2,86 @@ goog.provide('ascii.StateController');
goog.require('ascii.Vector');
/**
* Common interface for different drawing functions, e.g. box, line, etc.
* @interface
*/
function DrawFunction() {}
/** Start of drawing. @param {ascii.Vector} position */
DrawFunction.prototype.start = function(position) {};
/** Drawing move. @param {ascii.Vector} position */
DrawFunction.prototype.move = function(position) {};
/** End of drawing. @param {ascii.Vector} position */
DrawFunction.prototype.end = function(position) {};
/**
* @constructor
* @implements {DrawFunction}
* @param {ascii.State} state
*/
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();
};
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.drawValue(new ascii.Vector(x1, y1), '+');
this.state.drawValue(new ascii.Vector(x1, y2), '+');
this.state.drawValue(new ascii.Vector(x2, y1), '+');
this.state.drawValue(new ascii.Vector(x2, y2), '+');
for (var x = x1 + 1; x < x2; x++) {
this.state.drawValue(new ascii.Vector(x, y1), '\u2014');
this.state.drawValue(new ascii.Vector(x, y2), '\u2014');
}
for (var y = y1 + 1; y < y2; y++) {
this.state.drawValue(new ascii.Vector(x1, y), '|');
this.state.drawValue(new ascii.Vector(x2, y), '|');
}
};
/**
* @constructor
* @implements {DrawFunction}
* @param {ascii.State} state
*/
function DrawFreeform(state) {
this.state = state;
}
DrawFreeform.prototype.start = function(position) {
this.state.drawValue(position, 'O');
};
DrawFreeform.prototype.move = function(position) {
this.state.drawValue(position, 'O');
};
DrawFreeform.prototype.end = function(position) {
this.state.commitDraw();
};
/**
* Handles management of the diagram state. Input events are cleaned in the
* parent controller and passed down to this class for dealing with drawing.
@ -11,6 +91,7 @@ goog.require('ascii.Vector');
*/
ascii.StateController = function(state) {
/** @type {ascii.State} */ this.state = state;
/** @type {DrawFunction} */ this.drawFunction = new DrawBox(state);
};
/**
@ -18,7 +99,7 @@ ascii.StateController = function(state) {
* @param {ascii.Vector} position
*/
ascii.StateController.prototype.handleDrawingPress = function(position) {
this.state.setValue(position, 'O');
this.drawFunction.start(position);
};
/**
@ -26,6 +107,7 @@ ascii.StateController.prototype.handleDrawingPress = function(position) {
* @param {ascii.Vector} position
*/
ascii.StateController.prototype.handleDrawingRelease = function(position) {
this.drawFunction.end(position);
};
/**
@ -33,6 +115,5 @@ ascii.StateController.prototype.handleDrawingRelease = function(position) {
* @param {ascii.Vector} position
*/
ascii.StateController.prototype.handleDrawingMove = function(position) {
this.state.setValue(position, 'O');
this.drawFunction.move(position);
};

View File

@ -9,6 +9,12 @@ goog.provide('ascii.State');
*/
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);
};
/**
@ -19,7 +25,8 @@ ascii.Cell = function() {
ascii.State = function() {
/** @type {Array.<Array.<ascii.Cell>>} */
this.cells = new Array(MAX_GRID_SIZE);
/** @type {boolean} */ this.dirty = true;
/** @type {Array.<ascii.Cell>} */
this.scratchCells = new Array();
for (var i = 0; i < this.cells.length; i++) {
this.cells[i] = new Array(MAX_GRID_SIZE);
@ -40,12 +47,45 @@ ascii.State.prototype.getCell = function(vector) {
};
/**
* Sets the cells value at the given position.
* Sets the cells value at the given position. Probably shouldn't
* be used directly in many cases. Used drawValue instead.
*
* @param {ascii.Vector} position
* @param {string} value
*/
ascii.State.prototype.setValue = function(position, value) {
this.getCell(position).value = value;
this.dirty = true;
};
/**
* Sets the cells scratch (uncommitted) value at the given position.
*
* @param {ascii.Vector} position
* @param {string} value
*/
ascii.State.prototype.drawValue = function(position, value) {
var cell = this.getCell(position);
this.scratchCells.push(cell);
cell.scratchValue = value;
};
/**
* Clears the current drawing scratchpad.
*/
ascii.State.prototype.clearDraw = function() {
for (var i in this.scratchCells) {
this.scratchCells[i].scratchValue = null;
}
this.scratchCells.length = 0;
};
/**
* Ends the current draw, commiting anything currently drawn the scratchpad.
*/
ascii.State.prototype.commitDraw = function() {
for (var i in this.scratchCells) {
this.scratchCells[i].value = this.scratchCells[i].getDrawValue();
this.scratchCells[i].scratchValue = null;
}
};

View File

@ -37,9 +37,8 @@ ascii.View.prototype.resizeCanvas = function() {
* Starts the animation loop for the canvas. Should only be called once.
*/
ascii.View.prototype.animate = function() {
if (this.dirty || this.state.dirty) {
if (this.dirty) {
this.dirty = false;
this.state.dirty = false;
this.render();
}
var view = this;
@ -48,6 +47,8 @@ ascii.View.prototype.animate = function() {
/**
* Renders the given state to the canvas.
* TODO: Room for efficiency here still. Drawing should be incremental,
* however performance is currently very acceptable on test devices.
*/
ascii.View.prototype.render = function() {
var context = this.context;
@ -95,8 +96,9 @@ ascii.View.prototype.render = function() {
this.context.font = '15px Courier New';
for (var i = startOffset.x; i < endOffset.x; i++) {
for (var j = startOffset.y; j < endOffset.y; j++) {
if (this.state.cells[i][j].value != null) {
context.fillText(this.state.cells[i][j].value,
var cellValue = this.state.cells[i][j].getDrawValue();
if (cellValue != null) {
context.fillText(cellValue,
i * CHARACTER_PIXELS - this.offset.x + 3,
j * CHARACTER_PIXELS - this.offset.y - 2);
}