diff --git a/src/js/devtools/DrawingTestRecorder.js b/src/js/devtools/DrawingTestRecorder.js index 3fa644c8..f83a50a6 100644 --- a/src/js/devtools/DrawingTestRecorder.js +++ b/src/js/devtools/DrawingTestRecorder.js @@ -142,6 +142,12 @@ recordEvent.type = 'instrumented-event'; recordEvent.methodName = methodName; recordEvent.args = Array.prototype.slice.call(args, 0); + + if (methodName === 'setPiskel' && args[1].noSnapshot) { + // Skip recording calls to setPiskel that don't trigger a save. + return; + } + this.events.push(recordEvent); } }; diff --git a/src/js/tools/transform/Crop.js b/src/js/tools/transform/Crop.js index c8d7b53d..6ae1f652 100644 --- a/src/js/tools/transform/Crop.js +++ b/src/js/tools/transform/Crop.js @@ -12,27 +12,47 @@ ]; }; - pskl.utils.inherit(ns.Crop, ns.AbstractTransformTool); + // This transform tool is the only one that adapts to the current selection and can't + // rely on the default AbstractTransformTool behavior. + pskl.utils.inherit(ns.Crop, pskl.tools.Tool); - ns.Crop.prototype.applyToolOnFrame_ = function (frame, altKey) { - var currentPiskel = pskl.app.piskelController.getPiskel(); - var frames = currentPiskel.getLayers().map(function (l) { - return l.getFrames(); - }).reduce(function (p, n) { - return p.concat(n); - }); + ns.Crop.prototype.applyTransformation = function (evt) { + var frames = this.getFrames_(); - var boundaries = pskl.tools.transform.TransformUtils.getBoundaries(frames); - if (boundaries.minx >= boundaries.maxx) { - return; + var boundaries; + if (pskl.app.selectionManager.currentSelection) { + // If we have a selection, we will compute the boundaries of the selection instead + // of looping on the frames. + boundaries = this.getBoundariesForSelection_(); + } else { + boundaries = pskl.tools.transform.TransformUtils.getBoundaries(frames); } + var applied = this.applyTool_(frames, boundaries); + if (applied) { + this.raiseSaveStateEvent({ + boundaries : boundaries + }); + } + }; + + ns.Crop.prototype.replay = function (frame, replayData) { + var frames = this.getFrames_(); + this.applyTool_(frames, replayData.boundaries); + }; + + ns.Crop.prototype.applyTool_ = function (frames, boundaries) { + if (boundaries.minx >= boundaries.maxx) { + return false; + } + + var currentPiskel = pskl.app.piskelController.getPiskel(); var width = 1 + boundaries.maxx - boundaries.minx; var height = 1 + boundaries.maxy - boundaries.miny; if (width === currentPiskel.getWidth() && height === currentPiskel.getHeight()) { // Do not perform an unnecessary resize if it's a noop. - return; + return false; } frames.forEach(function (frame) { @@ -46,10 +66,62 @@ resizeContent: false }); + // Clear the current selection. + $.publish(Events.SELECTION_DISMISSED); + + // Replace the current piskel with the resized version. pskl.app.piskelController.setPiskel(piskel, { preserveState: true, + // Saving is already handled by recording the transform tool action, no need for + // an expensive snapshot. noSnapshot: true }); + + return true; }; + /** + * Retrieve the list of frames for the current piskel in a single flat array. + */ + ns.Crop.prototype.getFrames_ = function () { + var currentPiskel = pskl.app.piskelController.getPiskel(); + + // Get all frames in a single array. + var frames = currentPiskel.getLayers().map(function (l) { + return l.getFrames(); + }).reduce(function (p, n) { + return p.concat(n); + }); + + return frames; + }; + + /** + * Retrieve a boundaries object {minx, maxx, miny, maxy} for the current selection. + */ + ns.Crop.prototype.getBoundariesForSelection_ = function () { + var selectionManager = pskl.app.selectionManager; + var pixels = selectionManager.currentSelection.pixels; + + // Fetch the first frame to perform out-of-bound checks. + var currentPiskel = pskl.app.piskelController.getPiskel(); + var exampleFrame = currentPiskel.getLayerAt(0).getFrameAt(0); + + // Anything different from Constants.TRANSPARENT_COLOR toInt(). + var FAKE_COLOR = 1; + // Create a fake frame reimplementing the forEachPixel API. + var selectionFrame = { + forEachPixel : function (callback) { + for (var i = 0; i < pixels.length ; i++) { + var pixel = pixels[i]; + // Selections might contain out of bound pixels, filter those out. + if (exampleFrame.containsPixel(pixel.col, pixel.row)) { + callback(FAKE_COLOR, pixel.col, pixel.row); + } + } + } + }; + + return pskl.tools.transform.TransformUtils.getBoundaries([selectionFrame]); + }; })(); diff --git a/src/js/tools/transform/TransformUtils.js b/src/js/tools/transform/TransformUtils.js index edea08d2..28158046 100644 --- a/src/js/tools/transform/TransformUtils.js +++ b/src/js/tools/transform/TransformUtils.js @@ -65,8 +65,8 @@ }, getBoundaries : function(frames) { - var minx = frames[0].width; - var miny = frames[0].height; + var minx = +Infinity; + var miny = +Infinity; var maxx = 0; var maxy = 0; diff --git a/test/drawing/DrawingTests.browser.js b/test/drawing/DrawingTests.browser.js index 895c567d..2ee94573 100644 --- a/test/drawing/DrawingTests.browser.js +++ b/test/drawing/DrawingTests.browser.js @@ -21,6 +21,8 @@ "transform.center.json", "transform.clone.once.json", "transform.clone.twice.undo.once.json", + "transform.crop.json", + "transform.crop.selection.json", "transform.rotate.once.alt.json", "transform.rotate.twice.undo.once.json", "transform.rotate.alt.twice.undo.once.json", diff --git a/test/drawing/DrawingTests.casper.js b/test/drawing/DrawingTests.casper.js index c7032c45..42bc0304 100644 --- a/test/drawing/DrawingTests.casper.js +++ b/test/drawing/DrawingTests.casper.js @@ -19,6 +19,8 @@ "transform.center.json", "transform.clone.once.json", "transform.clone.twice.undo.once.json", + "transform.crop.json", + "transform.crop.selection.json", "transform.rotate.once.alt.json", "transform.rotate.twice.undo.once.json", "transform.rotate.alt.twice.undo.once.json", diff --git a/test/drawing/tests/transform.crop.json b/test/drawing/tests/transform.crop.json new file mode 100644 index 00000000..26ba099f --- /dev/null +++ b/test/drawing/tests/transform.crop.json @@ -0,0 +1,185 @@ +{ + "events": [ + { + "event": { + "type": "mousedown", + "button": 0, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 1, + "y": 1 + }, + "type": "mouse-event" + }, + { + "event": { + "type": "mouseup", + "button": 0, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 1, + "y": 1 + }, + "type": "mouse-event" + }, + { + "event": { + "type": "mousedown", + "button": 0, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 1, + "y": 3 + }, + "type": "mouse-event" + }, + { + "event": { + "type": "mouseup", + "button": 0, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 1, + "y": 3 + }, + "type": "mouse-event" + }, + { + "event": { + "type": "mousedown", + "button": 0, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 2, + "y": 3 + }, + "type": "mouse-event" + }, + { + "event": { + "type": "mouseup", + "button": 0, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 2, + "y": 3 + }, + "type": "mouse-event" + }, + { + "event": { + "type": "mousedown", + "button": 0, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 3, + "y": 1 + }, + "type": "mouse-event" + }, + { + "event": { + "type": "mouseup", + "button": 0, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 3, + "y": 1 + }, + "type": "mouse-event" + }, + { + "type": "transformtool-event", + "toolId": "tool-crop", + "event": { + "shiftKey": false, + "altKey": false, + "ctrlKey": false + } + }, + { + "event": { + "type": "mousedown", + "button": 2, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 2, + "y": 0 + }, + "type": "mouse-event" + }, + { + "event": { + "type": "mouseup", + "button": 2, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 2, + "y": 0 + }, + "type": "mouse-event" + }, + { + "type": "transformtool-event", + "toolId": "tool-crop", + "event": { + "shiftKey": false, + "altKey": false, + "ctrlKey": false + } + }, + { + "type": "keyboard-event", + "event": { + "which": 90, + "shiftKey": false, + "altKey": false, + "ctrlKey": true, + "target": { + "nodeName": "BODY" + } + } + } + ], + "initialState": { + "size": { + "width": 4, + "height": 4 + }, + "primaryColor": "#000000", + "secondaryColor": "rgba(0, 0, 0, 0)", + "selectedTool": "tool-pen", + "penSize": 1 + }, + "png": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAAG0lEQVQYV2NkYGD4z8DAwMgAI0AMGAckA2YDACxpAgQTZnIjAAAAAElFTkSuQmCC" +} \ No newline at end of file diff --git a/test/drawing/tests/transform.crop.selection.json b/test/drawing/tests/transform.crop.selection.json new file mode 100644 index 00000000..17ad7ed2 --- /dev/null +++ b/test/drawing/tests/transform.crop.selection.json @@ -0,0 +1,274 @@ +{ + "events": [ + { + "event": { + "type": "mousedown", + "button": 0, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 0, + "y": 0 + }, + "type": "mouse-event" + }, + { + "event": { + "type": "mousemove", + "button": 0, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 0, + "y": 0 + }, + "type": "mouse-event" + }, + { + "event": { + "type": "mousemove", + "button": 0, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 1, + "y": 1 + }, + "type": "mouse-event" + }, + { + "event": { + "type": "mousemove", + "button": 0, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 2, + "y": 2 + }, + "type": "mouse-event" + }, + { + "event": { + "type": "mousemove", + "button": 0, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 3, + "y": 2 + }, + "type": "mouse-event" + }, + { + "event": { + "type": "mousemove", + "button": 0, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 3, + "y": 3 + }, + "type": "mouse-event" + }, + { + "event": { + "type": "mouseup", + "button": 0, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 3, + "y": 3 + }, + "type": "mouse-event" + }, + { + "event": { + "type": "mousedown", + "button": 0, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 0, + "y": 3 + }, + "type": "mouse-event" + }, + { + "event": { + "type": "mouseup", + "button": 0, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 0, + "y": 3 + }, + "type": "mouse-event" + }, + { + "type": "tool-event", + "toolId": "tool-lasso-select" + }, + { + "type": "keyboard-event", + "event": { + "which": 72, + "shiftKey": false, + "altKey": false, + "ctrlKey": false, + "target": { + "nodeName": "BODY" + } + } + }, + { + "event": { + "type": "mousedown", + "button": 0, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 1, + "y": 1 + }, + "type": "mouse-event" + }, + { + "event": { + "type": "mousemove", + "button": 0, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 1, + "y": 1 + }, + "type": "mouse-event" + }, + { + "event": { + "type": "mousemove", + "button": 0, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 1, + "y": 2 + }, + "type": "mouse-event" + }, + { + "event": { + "type": "mousemove", + "button": 0, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 2, + "y": 2 + }, + "type": "mouse-event" + }, + { + "event": { + "type": "mousemove", + "button": 0, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 2, + "y": 3 + }, + "type": "mouse-event" + }, + { + "event": { + "type": "mouseup", + "button": 0, + "shiftKey": false, + "altKey": false, + "ctrlKey": false + }, + "coords": { + "x": 2, + "y": 3 + }, + "type": "mouse-event" + }, + { + "type": "transformtool-event", + "toolId": "tool-crop", + "event": { + "shiftKey": false, + "altKey": false, + "ctrlKey": false + } + }, + { + "type": "keyboard-event", + "event": { + "which": 90, + "shiftKey": false, + "altKey": false, + "ctrlKey": true, + "target": { + "nodeName": "BODY" + } + } + }, + { + "type": "keyboard-event", + "event": { + "which": 89, + "shiftKey": false, + "altKey": false, + "ctrlKey": true, + "target": { + "nodeName": "BODY" + } + } + } + ], + "initialState": { + "size": { + "width": 4, + "height": 4 + }, + "primaryColor": "#000000", + "secondaryColor": "rgba(0, 0, 0, 0)", + "selectedTool": "tool-pen", + "penSize": 1 + }, + "png": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAADCAYAAAC56t6BAAAAGElEQVQYV2NkYGD4z8DAwMjIAAH/YQwGACFDAgOWGgpZAAAAAElFTkSuQmCC" +} \ No newline at end of file