asciiflow2/js-lib/draw.js

275 lines
7.9 KiB
JavaScript
Raw Normal View History

2014-01-19 14:23:17 +00:00
/**
* All drawing classes and functions.
*/
2014-01-19 14:23:17 +00:00
/**
* 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
*/
2014-01-19 14:23:17 +00:00
function drawLine(state, startPosition, endPosition, clockwise, opt_value) {
var value = opt_value || SPECIAL_VALUE;
var startX = Math.min(startPosition.x, endPosition.x);
var startY = Math.min(startPosition.y, endPosition.y);
var endX = Math.max(startPosition.x, endPosition.x);
var endY = Math.max(startPosition.y, endPosition.y);
var midX = clockwise ? endPosition.x : startPosition.x;
var midY = clockwise ? startPosition.y : endPosition.y;
while (startX++ < endX) {
state.drawValue(new ascii.Vector(startX, midY), value);
}
while (startY++ < endY) {
state.drawValue(new ascii.Vector(midX, startY), value);
}
2014-01-19 14:23:17 +00:00
state.drawValue(startPosition, value);
state.drawValue(endPosition, value);
state.drawValue(new ascii.Vector(midX, midY), value);
2014-01-19 14:23:17 +00:00
}
/**
* Common interface for different drawing functions, e.g. box, line, etc.
* @interface
*/
2014-01-19 14:23:17 +00:00
ascii.DrawFunction = function() {};
/** Start of drawing. @param {ascii.Vector} position */
2014-01-19 14:23:17 +00:00
ascii.DrawFunction.prototype.start = function(position) {};
/** Drawing move. @param {ascii.Vector} position */
2014-01-19 14:23:17 +00:00
ascii.DrawFunction.prototype.move = function(position) {};
/** End of drawing. @param {ascii.Vector} position */
2014-01-19 14:23:17 +00:00
ascii.DrawFunction.prototype.end = function(position) {};
2014-01-20 21:36:58 +00:00
/** Cursor for given cell.
* @param {ascii.Vector} position
* @return {string}
*/
ascii.DrawFunction.prototype.getCursor = function(position) {};
/**
* @constructor
2014-01-19 14:23:17 +00:00
* @implements {ascii.DrawFunction}
* @param {ascii.State} state
*/
2014-01-19 14:23:17 +00:00
ascii.DrawBox = function(state) {
this.state = state;
/** @type {ascii.Vector} */ this.startPosition = null;
2014-01-19 14:23:17 +00:00
};
2014-01-19 14:23:17 +00:00
ascii.DrawBox.prototype.start = function(position) {
this.startPosition = position;
};
2014-01-19 14:23:17 +00:00
ascii.DrawBox.prototype.move = function(position) {
this.endPosition = position;
this.state.clearDraw();
drawLine(this.state, this.startPosition, position, true);
drawLine(this.state, this.startPosition, position, false);
};
2014-01-19 14:23:17 +00:00
ascii.DrawBox.prototype.end = function(position) {
this.state.commitDraw();
};
2014-01-20 21:36:58 +00:00
ascii.DrawBox.prototype.getCursor = function(position) {
return 'crosshair';
};
/**
* @constructor
2014-01-19 14:23:17 +00:00
* @implements {ascii.DrawFunction}
* @param {ascii.State} state
*/
2014-01-19 14:23:17 +00:00
ascii.DrawLine = function(state) {
this.state = state;
/** @type {ascii.Vector} */ this.startPosition = null;
2014-01-19 14:23:17 +00:00
};
2014-01-19 14:23:17 +00:00
ascii.DrawLine.prototype.start = function(position) {
this.startPosition = position;
};
2014-01-19 14:23:17 +00:00
ascii.DrawLine.prototype.move = function(position) {
this.state.clearDraw();
// Try to infer line orientation.
2014-01-19 14:23:17 +00:00
// TODO: Split the line into two lines if we can't satisfy both ends.
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);
};
2014-01-19 14:23:17 +00:00
ascii.DrawLine.prototype.end = function(position) {
this.state.commitDraw();
};
2014-01-20 21:36:58 +00:00
ascii.DrawLine.prototype.getCursor = function(position) {
return 'crosshair';
};
/**
* @constructor
2014-01-19 14:23:17 +00:00
* @implements {ascii.DrawFunction}
* @param {ascii.State} state
* @param {?string} value
*/
2014-01-19 14:23:17 +00:00
ascii.DrawFreeform = function(state, value) {
this.state = state;
this.value = value;
2014-01-19 14:23:17 +00:00
};
2014-01-19 14:23:17 +00:00
ascii.DrawFreeform.prototype.start = function(position) {
this.state.drawValue(position, this.value);
};
2014-01-19 14:23:17 +00:00
ascii.DrawFreeform.prototype.move = function(position) {
this.state.drawValue(position, this.value);
};
2014-01-19 14:23:17 +00:00
ascii.DrawFreeform.prototype.end = function(position) {
this.state.commitDraw();
};
2014-01-20 21:36:58 +00:00
ascii.DrawFreeform.prototype.getCursor = function(position) {
return 'crosshair';
};
/**
* @constructor
* @implements {ascii.DrawFunction}
* @param {ascii.State} state
*/
ascii.DrawErase = function(state) {
this.state = state;
this.startPosition = null;
this.endPosition = null;
};
ascii.DrawErase.prototype.start = function(position) {
this.startPosition = position;
this.move(position);
};
ascii.DrawErase.prototype.move = function(position) {
this.state.clearDraw();
this.endPosition = position;
var startX = Math.min(this.startPosition.x, this.endPosition.x);
var startY = Math.min(this.startPosition.y, this.endPosition.y);
var endX = Math.max(this.startPosition.x, this.endPosition.x);
var endY = Math.max(this.startPosition.y, this.endPosition.y);
for (var i = startX; i < endX; i++) {
for (var j = startY; j < endY; j++) {
this.state.drawValue(new ascii.Vector(i, j), ' ');
}
}
};
ascii.DrawErase.prototype.end = function(position) {
this.state.commitDraw();
};
ascii.DrawErase.prototype.getCursor = function(position) {
return 'crosshair';
};
/**
* @constructor
2014-01-19 14:23:17 +00:00
* @implements {ascii.DrawFunction}
* @param {ascii.State} state
*/
2014-01-19 14:23:17 +00:00
ascii.DrawMove = function(state) {
this.state = state;
this.ends = null;
2014-01-19 14:23:17 +00:00
};
2014-01-19 14:23:17 +00:00
ascii.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),
2014-01-19 14:23:17 +00:00
new ascii.Vector(0, -1)];
var ends = [];
for (var i in directions) {
var midPoints = this.followLine(position, directions[i]);
for (var k in midPoints) {
var midPoint = midPoints[k];
// 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 ||
directions[i].add(directions[j]).length() == 2) {
// Don't go back on ourselves, or don't carry on in same direction.
continue;
}
2014-01-19 14:23:17 +00:00
// On the second line we don't care about multiple junctions.
var endz = this.followLine(midPoint, directions[j]);
// Ignore any directions that didn't go anywhere.
if (endz.length == 0 || midPoint.equals(endz[0])) {
continue;
}
ends.push({position: endz[0], clockwise: clockwise});
}
}
}
this.ends = ends;
// Clear all the lines so we can draw them afresh.
for (var i in ends) {
2014-01-19 14:23:17 +00:00
drawLine(this.state, position, ends[i].position, ends[i].clockwise, ' ');
}
this.state.commitDraw();
// Redraw the new lines after we have cleared the existing ones.
this.move(position);
};
2014-01-19 14:23:17 +00:00
ascii.DrawMove.prototype.move = function(position) {
this.state.clearDraw();
for (var i in this.ends) {
2014-01-19 14:23:17 +00:00
drawLine(this.state, position, this.ends[i].position,
this.ends[i].clockwise);
}
};
2014-01-19 14:23:17 +00:00
ascii.DrawMove.prototype.end = function(position) {
this.state.commitDraw();
};
2014-01-19 14:23:17 +00:00
ascii.DrawMove.prototype.followLine = function(startPosition, direction) {
var endPosition = startPosition.clone();
var junctions = [];
while (true) {
var nextEnd = endPosition.add(direction);
2014-01-19 14:23:17 +00:00
if (!this.state.getCell(endPosition).isSpecial() ||
!this.state.getCell(nextEnd).isSpecial()) {
return junctions;
}
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)) {
junctions.push(endPosition);
}
}
};
2014-01-20 21:36:58 +00:00
ascii.DrawMove.prototype.getCursor = function(position) {
if (this.state.getCell(position).isSpecial()) {
return 'pointer';
} else {
return 'default';
}
};