mirror of
https://github.com/piskelapp/piskel.git
synced 2023-08-10 21:12:52 +03:00
Issue #311 : Add lasso tool. Implementation and cleanup
This commit is contained in:
@@ -163,7 +163,8 @@
|
|||||||
cursor: url(../img/icons/hand.png) 7 7, pointer;
|
cursor: url(../img/icons/hand.png) 7 7, pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tool-rectangle-select .drawing-canvas-container:hover {
|
.tool-rectangle-select .drawing-canvas-container:hover,
|
||||||
|
.tool-lasso-select .drawing-canvas-container:hover {
|
||||||
cursor: crosshair;
|
cursor: crosshair;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,9 @@
|
|||||||
toDescriptor('rectangle', 'R', new pskl.tools.drawing.Rectangle()),
|
toDescriptor('rectangle', 'R', new pskl.tools.drawing.Rectangle()),
|
||||||
toDescriptor('circle', 'C', new pskl.tools.drawing.Circle()),
|
toDescriptor('circle', 'C', new pskl.tools.drawing.Circle()),
|
||||||
toDescriptor('move', 'M', new pskl.tools.drawing.Move()),
|
toDescriptor('move', 'M', new pskl.tools.drawing.Move()),
|
||||||
toDescriptor('rectangleSelect', 'S', new pskl.tools.drawing.RectangleSelect()),
|
toDescriptor('rectangleSelect', 'S', new pskl.tools.drawing.selection.RectangleSelect()),
|
||||||
toDescriptor('lassoSelect', 'S', new pskl.tools.drawing.LassoSelect()),
|
toDescriptor('lassoSelect', 'H', new pskl.tools.drawing.selection.LassoSelect()),
|
||||||
toDescriptor('shapeSelect', 'Z', new pskl.tools.drawing.ShapeSelect()),
|
toDescriptor('shapeSelect', 'Z', new pskl.tools.drawing.selection.ShapeSelect()),
|
||||||
toDescriptor('lighten', 'U', new pskl.tools.drawing.Lighten()),
|
toDescriptor('lighten', 'U', new pskl.tools.drawing.Lighten()),
|
||||||
toDescriptor('dithering', 'T', new pskl.tools.drawing.DitheringTool()),
|
toDescriptor('dithering', 'T', new pskl.tools.drawing.DitheringTool()),
|
||||||
toDescriptor('colorPicker', 'O', new pskl.tools.drawing.ColorPicker())
|
toDescriptor('colorPicker', 'O', new pskl.tools.drawing.ColorPicker())
|
||||||
@@ -97,22 +97,12 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
ns.ToolController.prototype.onKeyboardShortcut_ = function(charkey) {
|
ns.ToolController.prototype.onKeyboardShortcut_ = function(charkey) {
|
||||||
|
|
||||||
var tools = [];
|
|
||||||
for (var i = 0 ; i < this.tools.length ; i++) {
|
for (var i = 0 ; i < this.tools.length ; i++) {
|
||||||
var tool = this.tools[i];
|
var tool = this.tools[i];
|
||||||
if (tool.shortcut.toLowerCase() === charkey.toLowerCase()) {
|
if (tool.shortcut.toLowerCase() === charkey.toLowerCase()) {
|
||||||
tools.push(tool);
|
this.selectTool_(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) {
|
ns.ToolController.prototype.getToolById_ = function (toolId) {
|
||||||
|
|||||||
73
src/js/selection/LassoSelection.js
Normal file
73
src/js/selection/LassoSelection.js
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
(function () {
|
||||||
|
var ns = $.namespace('pskl.selection');
|
||||||
|
|
||||||
|
var OUTSIDE = -1;
|
||||||
|
var INSIDE = 1;
|
||||||
|
var VISITED = 2;
|
||||||
|
|
||||||
|
ns.LassoSelection = function (pixels, frame) {
|
||||||
|
// transform the selected pixels array to a Map to get a faster lookup
|
||||||
|
this.pixelsMap = {};
|
||||||
|
pixels.forEach(function (pixel) {
|
||||||
|
this.setPixelInMap_(pixel, INSIDE);
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
this.pixels = this.getLassoPixels_(frame);
|
||||||
|
};
|
||||||
|
|
||||||
|
pskl.utils.inherit(ns.LassoSelection, ns.BaseSelection);
|
||||||
|
|
||||||
|
ns.LassoSelection.prototype.getLassoPixels_ = function (frame) {
|
||||||
|
var lassoPixels = [];
|
||||||
|
|
||||||
|
frame.forEachPixel(function (color, pixelCol, pixelRow) {
|
||||||
|
var pixel = {col : pixelCol, row : pixelRow};
|
||||||
|
if (this.isInSelection_(pixel, frame)) {
|
||||||
|
lassoPixels.push(pixel);
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
return lassoPixels;
|
||||||
|
};
|
||||||
|
|
||||||
|
ns.LassoSelection.prototype.isInSelection_ = function (pixel, frame) {
|
||||||
|
var alreadyVisited = this.getPixelInMap_(pixel);
|
||||||
|
if (!alreadyVisited) {
|
||||||
|
this.visitPixel_(pixel, frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getPixelInMap_(pixel) == INSIDE;
|
||||||
|
};
|
||||||
|
|
||||||
|
ns.LassoSelection.prototype.visitPixel_ = function (pixel, frame) {
|
||||||
|
var frameBorderReached = false;
|
||||||
|
var visitedPixels = pskl.PixelUtils.visitConnectedPixels(pixel, frame, function (connectedPixel) {
|
||||||
|
var alreadyVisited = this.getPixelInMap_(connectedPixel);
|
||||||
|
if (alreadyVisited) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!frame.containsPixel(connectedPixel.col, connectedPixel.row)) {
|
||||||
|
frameBorderReached = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setPixelInMap_(connectedPixel, VISITED);
|
||||||
|
return true;
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
visitedPixels.forEach(function (visitedPixel) {
|
||||||
|
this.setPixelInMap_(visitedPixel, frameBorderReached ? OUTSIDE : INSIDE);
|
||||||
|
}.bind(this));
|
||||||
|
};
|
||||||
|
|
||||||
|
ns.LassoSelection.prototype.setPixelInMap_ = function (pixel, value) {
|
||||||
|
this.pixelsMap[pixel.col] = this.pixelsMap[pixel.col] || {};
|
||||||
|
this.pixelsMap[pixel.col][pixel.row] = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
ns.LassoSelection.prototype.getPixelInMap_ = function (pixel) {
|
||||||
|
return this.pixelsMap[pixel.col] && this.pixelsMap[pixel.col][pixel.row];
|
||||||
|
};
|
||||||
|
|
||||||
|
})();
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
ns.SelectionManager.prototype.onToolSelected_ = function(evt, tool) {
|
ns.SelectionManager.prototype.onToolSelected_ = function(evt, tool) {
|
||||||
var isSelectionTool = tool instanceof pskl.tools.drawing.BaseSelect;
|
var isSelectionTool = tool instanceof pskl.tools.drawing.selection.BaseSelect;
|
||||||
if (!isSelectionTool) {
|
if (!isSelectionTool) {
|
||||||
this.cleanSelection_();
|
this.cleanSelection_();
|
||||||
}
|
}
|
||||||
|
|||||||
49
src/js/tools/drawing/selection/AbstractDragSelect.js
Normal file
49
src/js/tools/drawing/selection/AbstractDragSelect.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
(function () {
|
||||||
|
var ns = $.namespace('pskl.tools.drawing.selection');
|
||||||
|
|
||||||
|
ns.AbstractDragSelect = function () {
|
||||||
|
ns.BaseSelect.call(this);
|
||||||
|
this.hasSelection = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
pskl.utils.inherit(ns.AbstractDragSelect, ns.BaseSelect);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
ns.AbstractDragSelect.prototype.onSelectStart_ = function (col, row, color, frame, overlay) {
|
||||||
|
if (this.hasSelection) {
|
||||||
|
this.hasSelection = false;
|
||||||
|
overlay.clear();
|
||||||
|
$.publish(Events.SELECTION_DISMISSED);
|
||||||
|
} else {
|
||||||
|
this.hasSelection = true;
|
||||||
|
this.startDragSelection_(col, row);
|
||||||
|
overlay.setPixel(col, row, this.getTransparentVariant_(Constants.SELECTION_TRANSPARENT_COLOR));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ns.AbstractDragSelect.prototype.onSelect_ = function (col, row, color, frame, overlay) {
|
||||||
|
if (!this.hasSelection && (this.startCol !== col || this.startRow !== row)) {
|
||||||
|
this.hasSelection = true;
|
||||||
|
this.startDragSelection_(col, row);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hasSelection) {
|
||||||
|
this.updateDragSelection_(col, row, color, frame, overlay);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ns.AbstractDragSelect.prototype.onSelectEnd_ = function (col, row, color, frame, overlay) {
|
||||||
|
if (this.hasSelection) {
|
||||||
|
this.endDragSelection_(col, row, color, frame, overlay);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @protected */
|
||||||
|
ns.AbstractDragSelect.prototype.startDragSelection_ = function (col, row, color, frame, overlay) {};
|
||||||
|
/** @protected */
|
||||||
|
ns.AbstractDragSelect.prototype.updateDragSelection_ = function (col, row, color, frame, overlay) {};
|
||||||
|
/** @protected */
|
||||||
|
ns.AbstractDragSelect.prototype.endDragSelection_ = function (col, row, color, frame, overlay) {};
|
||||||
|
})();
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
* @require pskl.utils
|
* @require pskl.utils
|
||||||
*/
|
*/
|
||||||
(function() {
|
(function() {
|
||||||
var ns = $.namespace('pskl.tools.drawing');
|
var ns = $.namespace('pskl.tools.drawing.selection');
|
||||||
|
|
||||||
ns.BaseSelect = function() {
|
ns.BaseSelect = function() {
|
||||||
this.secondaryToolId = pskl.tools.drawing.Move.TOOL_ID;
|
this.secondaryToolId = pskl.tools.drawing.Move.TOOL_ID;
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
pskl.utils.inherit(ns.BaseSelect, ns.BaseTool);
|
pskl.utils.inherit(ns.BaseSelect, pskl.tools.drawing.BaseTool);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
|
|||||||
@@ -4,179 +4,59 @@
|
|||||||
* @require pskl.utils
|
* @require pskl.utils
|
||||||
*/
|
*/
|
||||||
(function() {
|
(function() {
|
||||||
var ns = $.namespace('pskl.tools.drawing');
|
var ns = $.namespace('pskl.tools.drawing.selection');
|
||||||
|
|
||||||
ns.LassoSelect = function() {
|
ns.LassoSelect = function() {
|
||||||
this.toolId = 'tool-lasso-select';
|
this.toolId = 'tool-lasso-select';
|
||||||
|
|
||||||
this.helpText = 'Lasso selection';
|
this.helpText = 'Lasso selection';
|
||||||
|
|
||||||
ns.BaseSelect.call(this);
|
ns.AbstractDragSelect.call(this);
|
||||||
this.hasSelection = false;
|
|
||||||
|
|
||||||
this.selectionOrigin_ = null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pskl.utils.inherit(ns.LassoSelect, ns.BaseSelect);
|
pskl.utils.inherit(ns.LassoSelect, ns.AbstractDragSelect);
|
||||||
|
|
||||||
/**
|
ns.LassoSelect.prototype.startDragSelection_ = function (col, row) {
|
||||||
* @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}];
|
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.previousCol = col;
|
||||||
this.previousRow = row;
|
this.previousRow = row;
|
||||||
|
$.publish(Events.DRAG_START, [col, row]);
|
||||||
|
};
|
||||||
|
|
||||||
// join lasso tail with origin
|
ns.LassoSelect.prototype.updateDragSelection_ = function (col, row, color, frame, overlay) {
|
||||||
var additionnalPixels = this.getLinePixels_(col, this.selectionOrigin_.col, row, this.selectionOrigin_.row);
|
col = pskl.utils.Math.minmax(col, 0, frame.getWidth() - 1);
|
||||||
|
row = pskl.utils.Math.minmax(row, 0, frame.getHeight() - 1);
|
||||||
|
this.addPixelToSelection_(col, row, frame);
|
||||||
|
var additionnalPixels = this.getLinePixels_(col, this.startCol, row, this.startRow);
|
||||||
|
|
||||||
overlay.clear();
|
// during the selection, create simple ShapeSelection, containing only the pixels hovered by the user
|
||||||
this.selection = new pskl.selection.ShapeSelection(this.pixels.concat(additionnalPixels));
|
this.selection = new pskl.selection.ShapeSelection(this.pixels.concat(additionnalPixels));
|
||||||
$.publish(Events.SELECTION_CREATED, [this.selection]);
|
$.publish(Events.SELECTION_CREATED, [this.selection]);
|
||||||
|
|
||||||
|
overlay.clear();
|
||||||
this.drawSelectionOnOverlay_(overlay);
|
this.drawSelectionOnOverlay_(overlay);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ns.LassoSelect.prototype.onSelectEnd_ = function (col, row, color, frame, overlay) {
|
ns.LassoSelect.prototype.endDragSelection_ = function (col, row, color, frame, overlay) {
|
||||||
if (this.hasSelection) {
|
col = pskl.utils.Math.minmax(col, 0, frame.getWidth() - 1);
|
||||||
if ((Math.abs(col - this.previousCol) > 1) || (Math.abs(row - this.previousRow) > 1)) {
|
row = pskl.utils.Math.minmax(row, 0, frame.getHeight() - 1);
|
||||||
// The pen movement is too fast for the mousemove frequency, there is a gap between the
|
this.addPixelToSelection_(col, row, frame);
|
||||||
// current point and the previously drawn one.
|
var additionnalPixels = this.getLinePixels_(col, this.startCol, row, this.startRow);
|
||||||
// We fill the gap by calculating missing dots (simple linear interpolation) and draw them.
|
|
||||||
|
// finalize the selection, add all pixels contained inside the shape drawn by the user to the selection
|
||||||
|
this.selection = new pskl.selection.LassoSelection(this.pixels.concat(additionnalPixels), frame);
|
||||||
|
$.publish(Events.SELECTION_CREATED, [this.selection]);
|
||||||
|
|
||||||
|
overlay.clear();
|
||||||
|
this.drawSelectionOnOverlay_(overlay);
|
||||||
|
|
||||||
|
$.publish(Events.DRAG_END, [col, row]);
|
||||||
|
};
|
||||||
|
|
||||||
|
ns.LassoSelect.prototype.addPixelToSelection_ = function (col, row, frame) {
|
||||||
var interpolatedPixels = this.getLinePixels_(col, this.previousCol, row, this.previousRow);
|
var interpolatedPixels = this.getLinePixels_(col, this.previousCol, row, this.previousRow);
|
||||||
this.pixels = this.pixels.concat(interpolatedPixels);
|
this.pixels = this.pixels.concat(interpolatedPixels);
|
||||||
} else {
|
|
||||||
this.pixels.push({col : col, row : row});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
this.previousCol = col;
|
||||||
var additionnalPixels = this.getLinePixels_(col, this.selectionOrigin_.col, row, this.selectionOrigin_.row);
|
this.previousRow = 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;
|
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -4,43 +4,19 @@
|
|||||||
* @require pskl.utils
|
* @require pskl.utils
|
||||||
*/
|
*/
|
||||||
(function() {
|
(function() {
|
||||||
var ns = $.namespace('pskl.tools.drawing');
|
var ns = $.namespace('pskl.tools.drawing.selection');
|
||||||
|
|
||||||
ns.RectangleSelect = function() {
|
ns.RectangleSelect = function() {
|
||||||
this.toolId = 'tool-rectangle-select';
|
this.toolId = 'tool-rectangle-select';
|
||||||
|
|
||||||
this.helpText = 'Rectangle selection';
|
this.helpText = 'Rectangle selection';
|
||||||
|
|
||||||
ns.BaseSelect.call(this);
|
ns.AbstractDragSelect.call(this);
|
||||||
this.hasSelection = false;
|
|
||||||
|
|
||||||
this.selectionOrigin_ = null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pskl.utils.inherit(ns.RectangleSelect, ns.BaseSelect);
|
pskl.utils.inherit(ns.RectangleSelect, ns.AbstractDragSelect);
|
||||||
|
|
||||||
/**
|
ns.RectangleSelect.prototype.startDragSelection_ = function (col, row) {
|
||||||
* @override
|
|
||||||
*/
|
|
||||||
ns.RectangleSelect.prototype.onSelectStart_ = function (col, row, frame, overlay) {
|
|
||||||
this.selectionOrigin_ = {
|
|
||||||
col : col,
|
|
||||||
row : row
|
|
||||||
};
|
|
||||||
if (this.hasSelection) {
|
|
||||||
this.hasSelection = false;
|
|
||||||
overlay.clear();
|
|
||||||
$.publish(Events.SELECTION_DISMISSED);
|
|
||||||
} else {
|
|
||||||
this.startSelection_(col, row);
|
|
||||||
overlay.setPixel(col, row, Constants.SELECTION_TRANSPARENT_COLOR);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ns.RectangleSelect.prototype.startSelection_ = function (col, row) {
|
|
||||||
this.hasSelection = true;
|
|
||||||
$.publish(Events.DRAG_START, [col, row]);
|
$.publish(Events.DRAG_START, [col, row]);
|
||||||
// Drawing the first point of the rectangle in the fake overlay canvas:
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,25 +25,16 @@
|
|||||||
* the current mouse coordiinate in sprite.
|
* the current mouse coordiinate in sprite.
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
ns.RectangleSelect.prototype.onSelect_ = function (col, row, frame, overlay) {
|
ns.RectangleSelect.prototype.updateDragSelection_ = function (col, row, color, frame, overlay) {
|
||||||
if (!this.hasSelection && (this.selectionOrigin_.col !== col || this.selectionOrigin_.row !== row)) {
|
|
||||||
this.startSelection_(col, row);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.hasSelection) {
|
|
||||||
overlay.clear();
|
overlay.clear();
|
||||||
this.selection = new pskl.selection.RectangularSelection(
|
this.selection = new pskl.selection.RectangularSelection(this.startCol, this.startRow, col, row);
|
||||||
this.startCol, this.startRow, col, row);
|
|
||||||
$.publish(Events.SELECTION_CREATED, [this.selection]);
|
$.publish(Events.SELECTION_CREATED, [this.selection]);
|
||||||
this.drawSelectionOnOverlay_(overlay);
|
this.drawSelectionOnOverlay_(overlay);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ns.RectangleSelect.prototype.onSelectEnd_ = function (col, row, frame, overlay) {
|
ns.RectangleSelect.prototype.onSelectEnd_ = function (col, row, color, frame, overlay) {
|
||||||
if (this.hasSelection) {
|
this.onSelect_(col, row, color, frame, overlay);
|
||||||
this.onSelect_(col, row, frame, overlay);
|
|
||||||
$.publish(Events.DRAG_END, [col, row]);
|
$.publish(Events.DRAG_END, [col, row]);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* @require pskl.utils
|
* @require pskl.utils
|
||||||
*/
|
*/
|
||||||
(function() {
|
(function() {
|
||||||
var ns = $.namespace('pskl.tools.drawing');
|
var ns = $.namespace('pskl.tools.drawing.selection');
|
||||||
|
|
||||||
ns.ShapeSelect = function() {
|
ns.ShapeSelect = function() {
|
||||||
this.toolId = 'tool-shape-select';
|
this.toolId = 'tool-shape-select';
|
||||||
|
|||||||
@@ -99,10 +99,6 @@
|
|||||||
* 13. Continue looping until Q is exhausted.
|
* 13. Continue looping until Q is exhausted.
|
||||||
* 14. Return.
|
* 14. Return.
|
||||||
*/
|
*/
|
||||||
var paintedPixels = [];
|
|
||||||
var queue = [];
|
|
||||||
var dy = [-1, 0, 1, 0];
|
|
||||||
var dx = [0, 1, 0, -1];
|
|
||||||
var targetColor;
|
var targetColor;
|
||||||
try {
|
try {
|
||||||
targetColor = frame.getPixel(col, row);
|
targetColor = frame.getPixel(col, row);
|
||||||
@@ -114,22 +110,45 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
queue.push({'col': col, 'row': row});
|
var paintedPixels = pskl.PixelUtils.visitConnectedPixels({col:col, row:row}, frame, function (pixel) {
|
||||||
|
if (frame.containsPixel(pixel.col, pixel.row) && frame.getPixel(pixel.col, pixel.row) == targetColor) {
|
||||||
|
frame.setPixel(pixel.col, pixel.row, replacementColor);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
return paintedPixels;
|
||||||
|
},
|
||||||
|
|
||||||
|
visitConnectedPixels : function (pixel, frame, pixelVisitor) {
|
||||||
|
var col = pixel.col;
|
||||||
|
var row = pixel.row;
|
||||||
|
|
||||||
|
var queue = [];
|
||||||
|
var visitedPixels = [];
|
||||||
|
var dy = [-1, 0, 1, 0];
|
||||||
|
var dx = [0, 1, 0, -1];
|
||||||
|
|
||||||
|
queue.push(pixel);
|
||||||
|
visitedPixels.push(pixel);
|
||||||
|
pixelVisitor(pixel);
|
||||||
|
|
||||||
var loopCount = 0;
|
var loopCount = 0;
|
||||||
var cellCount = frame.getWidth() * frame.getHeight();
|
var cellCount = frame.getWidth() * frame.getHeight();
|
||||||
while (queue.length > 0) {
|
while (queue.length > 0) {
|
||||||
loopCount ++;
|
loopCount ++;
|
||||||
|
|
||||||
var currentItem = queue.pop();
|
var currentItem = queue.pop();
|
||||||
frame.setPixel(currentItem.col, currentItem.row, replacementColor);
|
|
||||||
paintedPixels.push({'col': currentItem.col, 'row': currentItem.row});
|
|
||||||
|
|
||||||
for (var i = 0; i < 4; i++) {
|
for (var i = 0; i < 4; i++) {
|
||||||
var nextCol = currentItem.col + dx[i];
|
var nextCol = currentItem.col + dx[i];
|
||||||
var nextRow = currentItem.row + dy[i];
|
var nextRow = currentItem.row + dy[i];
|
||||||
try {
|
try {
|
||||||
if (frame.containsPixel(nextCol, nextRow) && frame.getPixel(nextCol, nextRow) == targetColor) {
|
var connectedPixel = {'col': nextCol, 'row': nextRow};
|
||||||
queue.push({'col': nextCol, 'row': nextRow});
|
var isValid = pixelVisitor(connectedPixel);
|
||||||
|
if (isValid) {
|
||||||
|
queue.push(connectedPixel);
|
||||||
|
visitedPixels.push(connectedPixel);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Frame out of bound exception.
|
// Frame out of bound exception.
|
||||||
@@ -142,7 +161,8 @@
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return paintedPixels;
|
|
||||||
|
return visitedPixels;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -72,6 +72,7 @@
|
|||||||
// Selection
|
// Selection
|
||||||
"js/selection/SelectionManager.js",
|
"js/selection/SelectionManager.js",
|
||||||
"js/selection/BaseSelection.js",
|
"js/selection/BaseSelection.js",
|
||||||
|
"js/selection/LassoSelection.js",
|
||||||
"js/selection/RectangularSelection.js",
|
"js/selection/RectangularSelection.js",
|
||||||
"js/selection/ShapeSelection.js",
|
"js/selection/ShapeSelection.js",
|
||||||
|
|
||||||
@@ -184,6 +185,7 @@
|
|||||||
"js/tools/drawing/Circle.js",
|
"js/tools/drawing/Circle.js",
|
||||||
"js/tools/drawing/Move.js",
|
"js/tools/drawing/Move.js",
|
||||||
"js/tools/drawing/selection/BaseSelect.js",
|
"js/tools/drawing/selection/BaseSelect.js",
|
||||||
|
"js/tools/drawing/selection/AbstractDragSelect.js",
|
||||||
"js/tools/drawing/selection/LassoSelect.js",
|
"js/tools/drawing/selection/LassoSelect.js",
|
||||||
"js/tools/drawing/selection/RectangleSelect.js",
|
"js/tools/drawing/selection/RectangleSelect.js",
|
||||||
"js/tools/drawing/selection/ShapeSelect.js",
|
"js/tools/drawing/selection/ShapeSelect.js",
|
||||||
|
|||||||
Reference in New Issue
Block a user