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

View File

@ -2,6 +2,86 @@ goog.provide('ascii.StateController');
goog.require('ascii.Vector'); 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 * Handles management of the diagram state. Input events are cleaned in the
* parent controller and passed down to this class for dealing with drawing. * parent controller and passed down to this class for dealing with drawing.
@ -11,6 +91,7 @@ goog.require('ascii.Vector');
*/ */
ascii.StateController = function(state) { ascii.StateController = function(state) {
/** @type {ascii.State} */ this.state = 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 * @param {ascii.Vector} position
*/ */
ascii.StateController.prototype.handleDrawingPress = function(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 * @param {ascii.Vector} position
*/ */
ascii.StateController.prototype.handleDrawingRelease = function(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 * @param {ascii.Vector} position
*/ */
ascii.StateController.prototype.handleDrawingMove = function(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() { ascii.Cell = function() {
/** @type {?string} */ this.value = null; /** @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() { ascii.State = function() {
/** @type {Array.<Array.<ascii.Cell>>} */ /** @type {Array.<Array.<ascii.Cell>>} */
this.cells = new Array(MAX_GRID_SIZE); 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++) { for (var i = 0; i < this.cells.length; i++) {
this.cells[i] = new Array(MAX_GRID_SIZE); 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 {ascii.Vector} position
* @param {string} value * @param {string} value
*/ */
ascii.State.prototype.setValue = function(position, value) { ascii.State.prototype.setValue = function(position, value) {
this.getCell(position).value = 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. * Starts the animation loop for the canvas. Should only be called once.
*/ */
ascii.View.prototype.animate = function() { ascii.View.prototype.animate = function() {
if (this.dirty || this.state.dirty) { if (this.dirty) {
this.dirty = false; this.dirty = false;
this.state.dirty = false;
this.render(); this.render();
} }
var view = this; var view = this;
@ -48,6 +47,8 @@ ascii.View.prototype.animate = function() {
/** /**
* Renders the given state to the canvas. * 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() { ascii.View.prototype.render = function() {
var context = this.context; var context = this.context;
@ -95,8 +96,9 @@ 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.cells[i][j].value != null) { var cellValue = this.state.cells[i][j].getDrawValue();
context.fillText(this.state.cells[i][j].value, if (cellValue != null) {
context.fillText(cellValue,
i * CHARACTER_PIXELS - this.offset.x + 3, i * CHARACTER_PIXELS - this.offset.x + 3,
j * CHARACTER_PIXELS - this.offset.y - 2); j * CHARACTER_PIXELS - this.offset.y - 2);
} }