asciiflow2/js-lib/state-controller.js

197 lines
5.7 KiB
JavaScript

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.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
* @implements {DrawFunction}
* @param {ascii.State} state
*/
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();
};
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}
* @param {ascii.State} state
* @param {?string} value
*/
function DrawFreeform(state, value) {
this.state = state;
this.value = value;
}
DrawFreeform.prototype.start = function(position) {
this.state.setValue(position, this.value);
};
DrawFreeform.prototype.move = function(position) {
this.state.setValue(position, this.value);
};
DrawFreeform.prototype.end = function(position) {
};
/**
* 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, 'O');
}.bind(this));
$('#erase-button').click(function(e) {
this.drawFunction = new DrawFreeform(state, null);
}.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);
};