Issue #311 : Add lasso tool initial commit

This commit is contained in:
jdescottes 2015-09-20 10:31:11 +02:00
parent 66fa71affd
commit f7592f864b
3 changed files with 195 additions and 1 deletions

View file

@ -17,6 +17,7 @@
toDescriptor('circle', 'C', new pskl.tools.drawing.Circle()),
toDescriptor('move', 'M', new pskl.tools.drawing.Move()),
toDescriptor('rectangleSelect', 'S', new pskl.tools.drawing.RectangleSelect()),
toDescriptor('lassoSelect', 'S', new pskl.tools.drawing.LassoSelect()),
toDescriptor('shapeSelect', 'Z', new pskl.tools.drawing.ShapeSelect()),
toDescriptor('lighten', 'U', new pskl.tools.drawing.Lighten()),
toDescriptor('dithering', 'T', new pskl.tools.drawing.DitheringTool()),
@ -96,12 +97,22 @@
};
ns.ToolController.prototype.onKeyboardShortcut_ = function(charkey) {
var tools = [];
for (var i = 0 ; i < this.tools.length ; i++) {
var tool = this.tools[i];
if (tool.shortcut.toLowerCase() === charkey.toLowerCase()) {
this.selectTool_(tool);
tools.push(tool);
}
}
if (tools.length === 0) {
return;
}
var currentToolIndex = tools.indexOf(this.currentSelectedTool);
this.selectTool_(tools[(currentToolIndex+1) % tools.length]);
};
ns.ToolController.prototype.getToolById_ = function (toolId) {

View file

@ -0,0 +1,182 @@
/**
* @provide pskl.tools.drawing.ShapeSelect
*
* @require pskl.utils
*/
(function() {
var ns = $.namespace('pskl.tools.drawing');
ns.LassoSelect = function() {
this.toolId = 'tool-lasso-select';
this.helpText = 'Lasso selection';
ns.BaseSelect.call(this);
this.hasSelection = false;
this.selectionOrigin_ = null;
};
pskl.utils.inherit(ns.LassoSelect, ns.BaseSelect);
/**
* @override
*/
ns.LassoSelect.prototype.onSelectStart_ = function (col, row, color, frame, overlay) {
this.selectionOrigin_ = {
col : col,
row : row
};
this.previousCol = col;
this.previousRow = row;
if (this.hasSelection) {
this.hasSelection = false;
overlay.clear();
$.publish(Events.SELECTION_DISMISSED);
} else {
this.startSelection_(col, row);
overlay.setPixel(col, row, color);
}
};
ns.LassoSelect.prototype.startSelection_ = function (col, row) {
this.hasSelection = true;
this.pixels = [{col : col, row : row}];
$.publish(Events.DRAG_START, [col, row]);
// Drawing the first point of the rectangle in the fake overlay canvas:
};
/**
* When creating the rectangle selection, we clear the current overlayFrame and
* redraw the current rectangle based on the orgin coordinate and
* the current mouse coordiinate in sprite.
* @override
*/
ns.LassoSelect.prototype.onSelect_ = function (col, row, color, frame, overlay) {
if (!this.hasSelection && (this.selectionOrigin_.col !== col || this.selectionOrigin_.row !== row)) {
this.startSelection_(col, row);
}
if (this.hasSelection) {
if ((Math.abs(col - this.previousCol) > 1) || (Math.abs(row - this.previousRow) > 1)) {
// The pen movement is too fast for the mousemove frequency, there is a gap between the
// current point and the previously drawn one.
// We fill the gap by calculating missing dots (simple linear interpolation) and draw them.
var interpolatedPixels = this.getLinePixels_(col, this.previousCol, row, this.previousRow);
this.pixels = this.pixels.concat(interpolatedPixels);
} else {
this.pixels.push({col : col, row : row});
}
this.previousCol = col;
this.previousRow = row;
// join lasso tail with origin
var additionnalPixels = this.getLinePixels_(col, this.selectionOrigin_.col, row, this.selectionOrigin_.row);
overlay.clear();
this.selection = new pskl.selection.ShapeSelection(this.pixels.concat(additionnalPixels));
$.publish(Events.SELECTION_CREATED, [this.selection]);
this.drawSelectionOnOverlay_(overlay);
}
};
ns.LassoSelect.prototype.onSelectEnd_ = function (col, row, color, frame, overlay) {
if (this.hasSelection) {
if ((Math.abs(col - this.previousCol) > 1) || (Math.abs(row - this.previousRow) > 1)) {
// The pen movement is too fast for the mousemove frequency, there is a gap between the
// current point and the previously drawn one.
// We fill the gap by calculating missing dots (simple linear interpolation) and draw them.
var interpolatedPixels = this.getLinePixels_(col, this.previousCol, row, this.previousRow);
this.pixels = this.pixels.concat(interpolatedPixels);
} else {
this.pixels.push({col : col, row : row});
}
var additionnalPixels = this.getLinePixels_(col, this.selectionOrigin_.col, row, this.selectionOrigin_.row);
this.pixels = this.pixels.concat(additionnalPixels);
var shapePixels = [];
var pixelsMap = {};
this.pixels.forEach(function (p) {
pixelsMap[p.col] = pixelsMap[p.col] || {};
pixelsMap[p.col][p.row] = 1;
});
frame.forEachPixel(function (color, c, r) {
if(this.isInPoly_(c, r, pixelsMap, frame)) {
shapePixels.push({col : c, row : r});
}
}.bind(this));
this.pixels = this.pixels.concat(shapePixels);
this.selection = new pskl.selection.ShapeSelection(this.pixels);
$.publish(Events.SELECTION_CREATED, [this.selection]);
this.onSelect_(col, row, color, frame, overlay);
$.publish(Events.DRAG_END, [col, row]);
}
};
ns.LassoSelect.prototype.isInPoly_ = function (col, row, pixelsMap, frame) {
if (pixelsMap[col] && pixelsMap[col][row]) {
// already marked
return pixelsMap[col][row] == 1;
}
var paintedPixels = [];
var queue = [];
var dy = [-1, 0, 1, 0];
var dx = [0, 1, 0, -1];
queue.push({'col': col, 'row': row});
var isOut = false;
var loopCount = 0;
var cellCount = frame.getWidth() * frame.getHeight();
while (queue.length > 0) {
loopCount ++;
var currentItem = queue.pop();
paintedPixels.push({'col': currentItem.col, 'row': currentItem.row});
for (var i = 0; i < 4; i++) {
var nextCol = currentItem.col + dx[i];
var nextRow = currentItem.row + dy[i];
try {
var isMarked = pixelsMap[nextCol] && pixelsMap[nextCol][nextRow];
if (frame.containsPixel(nextCol, nextRow) && !isMarked && !this.isInPixels_(nextCol, nextRow, pixelsMap)) {
queue.push({'col': nextCol, 'row': nextRow});
pixelsMap[nextCol] = pixelsMap[nextCol] || {};
pixelsMap[nextCol][nextRow] = 2;
if ((nextCol === 0 || nextCol == frame.getWidth() - 1) || (nextRow === 0 || nextRow == frame.getHeight() - 1)) {
isOut= true;
}
}
} catch (e) {
// Frame out of bound exception.
}
}
// Security loop breaker:
if (loopCount > 10 * cellCount) {
console.log('loop breaker called');
break;
}
}
paintedPixels.forEach(function (p) {
pixelsMap[p.col] = pixelsMap[p.col] || {};
pixelsMap[p.col][p.row] = isOut ? -1 : 1;
});
return !isOut;
};
ns.LassoSelect.prototype.isInPixels_ = function (col, row, pixelsMap) {
return pixelsMap[col] && pixelsMap[col][row] === 1;
};
})();

View file

@ -184,6 +184,7 @@
"js/tools/drawing/Circle.js",
"js/tools/drawing/Move.js",
"js/tools/drawing/selection/BaseSelect.js",
"js/tools/drawing/selection/LassoSelect.js",
"js/tools/drawing/selection/RectangleSelect.js",
"js/tools/drawing/selection/ShapeSelect.js",
"js/tools/drawing/ColorPicker.js",