diff --git a/js/Events.js b/js/Events.js index ce13ed7a..e6224a0e 100644 --- a/js/Events.js +++ b/js/Events.js @@ -3,7 +3,6 @@ Events = { TOOL_SELECTED : "TOOL_SELECTED", TOOL_RELEASED : "TOOL_RELEASED", COLOR_SELECTED: "COLOR_SELECTED", - COLOR_USED: "COLOR_USED", /** * When this event is emitted, a request is sent to the localstorage @@ -13,7 +12,6 @@ Events = { LOCALSTORAGE_REQUEST: "LOCALSTORAGE_REQUEST", CANVAS_RIGHT_CLICKED: "CANVAS_RIGHT_CLICKED", - CANVAS_RIGHT_CLICK_RELEASED: "CANVAS_RIGHT_CLICK_RELEASED", /** * Event to request a refresh of the display. diff --git a/js/HistoryManager.js b/js/HistoryManager.js index 4a3c5c4b..d6a92208 100644 --- a/js/HistoryManager.js +++ b/js/HistoryManager.js @@ -1,40 +1,40 @@ (function () { var ns = $.namespace("pskl"); - ns.HistoryManager = function () {}; + ns.HistoryManager = function (framesheet) { + this.framesheet = framesheet; + }; ns.HistoryManager.prototype.init = function () { - document.body.addEventListener('keyup', this.onKeyup.bind(this)); - $.subscribe(Events.TOOL_RELEASED, this.saveState.bind(this)); + document.body.addEventListener('keyup', this.onKeyup.bind(this)); + $.subscribe(Events.TOOL_RELEASED, this.saveState.bind(this)); }; - ns.HistoryManager.prototype.saveState = function () { - piskel.getCurrentFrame().saveState(); - }; + ns.HistoryManager.prototype.saveState = function () { + this.framesheet.getCurrentFrame().saveState(); + }; ns.HistoryManager.prototype.onKeyup = function (evt) { - if (evt.ctrlKey && evt.keyCode == 90) { // CTRL + Z - this.undo(); - } + if (evt.ctrlKey && evt.keyCode == 90) { // CTRL + Z + this.undo(); + } - if (evt.ctrlKey && evt.keyCode == 89) { // CTRL+ Y - this.redo(); - } - }; + if (evt.ctrlKey && evt.keyCode == 89) { // CTRL+ Y + this.redo(); + } + }; - ns.HistoryManager.prototype.undo = function () { - piskel.getCurrentFrame().loadPreviousState(); - this.redraw(); - }; + ns.HistoryManager.prototype.undo = function () { + this.framesheet.getCurrentFrame().loadPreviousState(); + this.redraw(); + }; - ns.HistoryManager.prototype.redo = function () { - piskel.getCurrentFrame().loadNextState(); - this.redraw(); - }; + ns.HistoryManager.prototype.redo = function () { + this.framesheet.getCurrentFrame().loadNextState(); + this.redraw(); + }; - ns.HistoryManager.prototype.redraw = function () { - piskel.drawingController.renderFrame(); - piskel.previewsController.createPreviews(); - }; - - ns.HistoryManager = new ns.HistoryManager(); + ns.HistoryManager.prototype.redraw = function () { + this.framesheet.drawingController.renderFrame(); + this.framesheet.previewsController.createPreviews(); + }; })(); \ No newline at end of file diff --git a/js/LocalStorageService.js b/js/LocalStorageService.js index d697adf7..5397e433 100644 --- a/js/LocalStorageService.js +++ b/js/LocalStorageService.js @@ -43,8 +43,6 @@ pskl.LocalStorageService = (function() { */ var restoreFromLocalStorage_ = function() { frameSheet_.deserialize(window.localStorage['snapShot']); - // Model updated, redraw everything: - $.publish(Events.REFRESH); }; /** diff --git a/js/controller/DrawingController.js b/js/controller/DrawingController.js index 145d83d8..1e62ee61 100644 --- a/js/controller/DrawingController.js +++ b/js/controller/DrawingController.js @@ -1,8 +1,6 @@ (function () { var ns = $.namespace("pskl.controller"); - ns.DrawingController = function (frame, container, dpi) { - this.dpi = dpi; - + ns.DrawingController = function (framesheet, container, dpi) { // TODO(vincz): Store user prefs in a localstorage string ? var renderingOptions = { "dpi": dpi, @@ -12,29 +10,22 @@ /** * @public */ - this.frame = frame; + this.framesheet = framesheet; /** * @public */ - this.overlayFrame = pskl.model.Frame.createEmptyFromFrame(frame); + this.overlayFrame = pskl.model.Frame.createEmptyFromFrame(framesheet.getCurrentFrame()); /** * @private */ this.container = container; - this.renderer = new pskl.rendering.FrameRenderer( - this.container, - renderingOptions, - "drawing-canvas"); - - this.overlayRenderer = new pskl.rendering.FrameRenderer( - this.container, - renderingOptions, - "canvas-overlay"); + this.renderer = new pskl.rendering.FrameRenderer(this.container, renderingOptions, "drawing-canvas"); + this.overlayRenderer = new pskl.rendering.FrameRenderer(this.container, renderingOptions, "canvas-overlay"); - this.renderer.init(this.frame); + this.renderer.init(framesheet.getCurrentFrame()); this.overlayRenderer.init(this.overlayFrame); // State of drawing controller: @@ -48,19 +39,18 @@ this.initMouseBehavior(); $.subscribe(Events.TOOL_SELECTED, $.proxy(function(evt, toolBehavior) { + console.log("Tool selected: ", toolBehavior); + this.currentToolBehavior = toolBehavior; + }, this)); - console.log("Tool selected: ", toolBehavior); - this.currentToolBehavior = toolBehavior; - }, this)); - - $.subscribe(Events.COLOR_SELECTED, $.proxy(function(evt, color, isPrimary) { - console.log("Color selected: ", color); - if (isPrimary) { - this.primaryColor = color; - } else { - this.secondaryColor = color; - } - }, this)); + $.subscribe(Events.COLOR_SELECTED, $.proxy(function(evt, color, isPrimary) { + console.log("Color selected: ", color); + if (isPrimary) { + this.primaryColor = color; + } else { + this.secondaryColor = color; + } + }, this)); }; ns.DrawingController.prototype.initMouseBehavior = function() { @@ -84,14 +74,13 @@ $.publish(Events.CANVAS_RIGHT_CLICKED); } - var spriteCoordinate = this.getSpriteCoordinate(event); - //console.log("mousedown: col: " + spriteCoordinate.col + " - row: " + spriteCoordinate.row); + var coords = this.getSpriteCoordinates(event); this.currentToolBehavior.applyToolAt( - spriteCoordinate.col, - spriteCoordinate.row, + coords.col, coords.row, this.getCurrentColor_(), - this + this.framesheet.getCurrentFrame(), + this.overlayFrame ); $.publish(Events.LOCALSTORAGE_REQUEST); @@ -104,27 +93,20 @@ var currentTime = new Date().getTime(); // Throttling of the mousemove event: if ((currentTime - this.previousMousemoveTime) > 40 ) { - var spriteCoordinate = this.getSpriteCoordinate(event); + var coords = this.getSpriteCoordinates(event); if (this.isClicked) { this.currentToolBehavior.moveToolAt( - spriteCoordinate.col, - spriteCoordinate.row, + coords.col, coords.row, this.getCurrentColor_(), - this + this.framesheet.getCurrentFrame(), + this.overlayFrame ); - //console.log("mousemove: col: " + spriteCoordinate.col + " - row: " + spriteCoordinate.row); - // TODO(vincz): Find a way to move that to the model instead of being at the interaction level. // Eg when drawing, it may make sense to have it here. However for a non drawing tool, // you don't need to draw anything when mousemoving and you request useless localStorage. $.publish(Events.LOCALSTORAGE_REQUEST); - } else { - // debug mode to see the selected pixel - // this.clearOverlay(); - // this.overlayFrame.setPixel( spriteCoordinate.col,spriteCoordinate.row, "#ff0000"); - // this.renderOverlay(); } this.previousMousemoveTime = currentTime; } @@ -139,26 +121,20 @@ // the user was probably drawing on the canvas. // Note: The mousemove movement (and the mouseup) may end up outside // of the drawing canvas. - if(this.isRightClicked) { - $.publish(Events.CANVAS_RIGHT_CLICK_RELEASED); - } - this.isClicked = false; this.isRightClicked = false; - var spriteCoordinate = this.getSpriteCoordinate(event); + + var coords = this.getSpriteCoordinates(event); //console.log("mousemove: col: " + spriteCoordinate.col + " - row: " + spriteCoordinate.row); this.currentToolBehavior.releaseToolAt( - spriteCoordinate.col, - spriteCoordinate.row, + coords.col, coords.row, this.getCurrentColor_(), - this + this.framesheet.getCurrentFrame(), + this.overlayFrame ); $.publish(Events.TOOL_RELEASED); - - // TODO: Remove that when we have the centralized redraw loop - $.publish(Events.REDRAW_PREVIEWFILM); } }, @@ -176,7 +152,7 @@ /** * @private */ - ns.DrawingController.prototype.getSpriteCoordinate = function(event) { + ns.DrawingController.prototype.getSpriteCoordinates = function(event) { var coords = this.getRelativeCoordinates(event.clientX, event.clientY); return this.renderer.convertPixelCoordinatesIntoSpriteCoordinate(coords); }; @@ -219,17 +195,14 @@ }; ns.DrawingController.prototype.renderFrame = function () { - var serializedFrame = this.frame.serialize(); + var frame = this.framesheet.getCurrentFrame(); + var serializedFrame = frame.serialize(); if (this.serializedFrame != serializedFrame) { this.serializedFrame = serializedFrame - this.renderer.render(this.frame); + this.renderer.render(frame); } }; - ns.DrawingController.prototype.renderFramePixel = function (col, row) { - this.renderer.drawPixel(col, row, this.frame); - }; - ns.DrawingController.prototype.renderOverlay = function () { var serializedOverlay = this.overlayFrame.serialize(); if (this.serializedOverlay != serializedOverlay) { diff --git a/js/controller/PreviewFilmController.js b/js/controller/PreviewFilmController.js index 5ae04e03..389a12f8 100644 --- a/js/controller/PreviewFilmController.js +++ b/js/controller/PreviewFilmController.js @@ -6,11 +6,10 @@ this.framesheet = framesheet; this.container = container; - this.dirty = false; + this.redrawFlag = false; - $.subscribe(Events.REDRAW_PREVIEWFILM, $.proxy(function(evt) { - this.dirty = true; - }, this)); + $.subscribe(Events.TOOL_RELEASED, this.flagForRedraw_.bind(this)); + $.subscribe(Events.FRAMESHEET_RESET, this.flagForRedraw_.bind(this)); }; ns.PreviewFilmController.prototype.init = function() { @@ -20,19 +19,29 @@ ns.PreviewFilmController.prototype.addFrame = function () { this.framesheet.addEmptyFrame(); - piskel.setActiveFrame(this.framesheet.getFrameCount() - 1); + this.framesheet.setCurrentFrameIndex(this.framesheet.getFrameCount() - 1); + }; + + ns.PreviewFilmController.prototype.flagForRedraw_ = function () { + this.redrawFlag = true; }; ns.PreviewFilmController.prototype.render = function () { - if (!this.dirty) return - // TODO(vincz): Full redraw on any drawing modification, optimize. + if (this.redrawFlag) { + // TODO(vincz): Full redraw on any drawing modification, optimize. + this.createPreviews_(); + this.redrawFlag = false; + } + }; + + ns.PreviewFilmController.prototype.createPreviews_ = function () { this.container.html(""); var frameCount = this.framesheet.getFrameCount(); for (var i = 0, l = frameCount; i < l ; i++) { - this.container.append(this.createInterstitialTile_(i)); - this.container.append(this.createPreviewTile_(i, this.framesheet)); + this.container.append(this.createInterstitialTile_(i)); + this.container.append(this.createPreviewTile_(i)); } this.container.append(this.createInterstitialTile_(frameCount)); @@ -40,19 +49,18 @@ if(needDragndropBehavior) { this.initDragndropBehavior_(); } - this.dirty = false; }; /** * @private */ ns.PreviewFilmController.prototype.createInterstitialTile_ = function (tileNumber) { - var initerstitialTile = document.createElement("div"); - initerstitialTile.className = "interstitial-tile" - initerstitialTile.setAttribute("data-tile-type", "interstitial"); - initerstitialTile.setAttribute("data-inject-drop-tile-at", tileNumber); + var interstitialTile = document.createElement("div"); + interstitialTile.className = "interstitial-tile" + interstitialTile.setAttribute("data-tile-type", "interstitial"); + interstitialTile.setAttribute("data-inject-drop-tile-at", tileNumber); - return initerstitialTile; + return interstitialTile; }; /** @@ -101,8 +109,8 @@ // inside the drag target. We normalize that by taking the correct ancestor: var originTile = $(event.srcElement).closest(".preview-tile"); var originFrameId = parseInt(originTile.data("tile-number"), 10); + var dropTarget = $(event.target); - if(dropTarget.data("tile-type") == "interstitial") { var targetInsertionId = parseInt(dropTarget.data("inject-drop-tile-at"), 10); // In case we drop outside of the tile container @@ -120,8 +128,7 @@ if(activeFrame > (this.framesheet.getFrameCount() - 1)) { activeFrame = targetInsertionId - 1; } - } - else { + } else { var targetSwapId = parseInt(dropTarget.data("tile-number"), 10); // In case we drop outside of the tile container if(isNaN(originFrameId) || isNaN(targetSwapId)) { @@ -135,26 +142,24 @@ $('#preview-list').removeClass("show-interstitial-tiles"); - // TODO(vincz): deprecate. - piskel.setActiveFrame(activeFrame); + this.framesheet.setCurrentFrameIndex(activeFrame); // TODO(vincz): move localstorage request to the model layer? $.publish(Events.LOCALSTORAGE_REQUEST); - }; /** * @private * TODO(vincz): clean this giant rendering function & remove listeners. */ - ns.PreviewFilmController.prototype.createPreviewTile_ = function(tileNumber, framesheet) { + ns.PreviewFilmController.prototype.createPreviewTile_ = function(tileNumber) { var currentFrame = this.framesheet.getFrameByIndex(tileNumber); var previewTileRoot = document.createElement("li"); var classname = "preview-tile"; previewTileRoot.setAttribute("data-tile-number", tileNumber); - if (piskel.getActiveFrameIndex() == tileNumber) { + if (this.framesheet.getCurrentFrame() == currentFrame) { classname += " selected"; } previewTileRoot.className = classname; @@ -166,25 +171,14 @@ canvasBackground.className = "canvas-background"; canvasContainer.appendChild(canvasBackground); - previewTileRoot.addEventListener('click', function(evt) { - // has not class tile-action: - if(!evt.target.classList.contains('tile-action')) { - piskel.setActiveFrame(tileNumber); - } - }); + previewTileRoot.addEventListener('click', this.onPreviewClick_.bind(this, tileNumber)); var canvasPreviewDuplicateAction = document.createElement("button"); canvasPreviewDuplicateAction.className = "tile-action" canvasPreviewDuplicateAction.innerHTML = "dup" - canvasPreviewDuplicateAction.addEventListener('click', function(evt) { - framesheet.duplicateFrameByIndex(tileNumber); - $.publish(Events.LOCALSTORAGE_REQUEST); // Should come from model - $.publish('SET_ACTIVE_FRAME', [tileNumber + 1]); - }); + canvasPreviewDuplicateAction.addEventListener('click', this.onAddButtonClick_.bind(this, tileNumber)); - //this.renderer.render(this.framesheet.getFrameByIndex(tileNumber), canvasPreview); - // TODO(vincz): Eventually optimize this part by not recreating a FrameRenderer. Note that the real optim // is to make this update function (#createPreviewTile) less aggressive. var renderingOptions = {"dpi": this.dpi }; @@ -198,14 +192,29 @@ var canvasPreviewDeleteAction = document.createElement("button"); canvasPreviewDeleteAction.className = "tile-action" canvasPreviewDeleteAction.innerHTML = "del" - canvasPreviewDeleteAction.addEventListener('click', function(evt) { - framesheet.removeFrameByIndex(tileNumber); - $.publish(Events.FRAMESHEET_RESET); - $.publish(Events.LOCALSTORAGE_REQUEST); // Should come from model - }); + canvasPreviewDeleteAction.addEventListener('click', this.onDeleteButtonClick_.bind(this, tileNumber)); previewTileRoot.appendChild(canvasPreviewDeleteAction); } return previewTileRoot; }; + + ns.PreviewFilmController.prototype.onPreviewClick_ = function (index, evt) { + // has not class tile-action: + if(!evt.target.classList.contains('tile-action')) { + this.framesheet.setCurrentFrameIndex(index); + } + }; + + ns.PreviewFilmController.prototype.onDeleteButtonClick_ = function (index, evt) { + this.framesheet.removeFrameByIndex(index); + $.publish(Events.FRAMESHEET_RESET); + $.publish(Events.LOCALSTORAGE_REQUEST); // Should come from model + }; + + ns.PreviewFilmController.prototype.onAddButtonClick_ = function (index, evt) { + this.framesheet.duplicateFrameByIndex(index); + $.publish(Events.LOCALSTORAGE_REQUEST); // Should come from model + this.framesheet.setCurrentFrameIndex(index + 1); + }; })(); \ No newline at end of file diff --git a/js/drawingtools/BaseTool.js b/js/drawingtools/BaseTool.js index 905aa162..84f96824 100644 --- a/js/drawingtools/BaseTool.js +++ b/js/drawingtools/BaseTool.js @@ -8,11 +8,11 @@ ns.BaseTool = function() {}; - ns.BaseTool.prototype.applyToolAt = function(col, row, frame) {}; + ns.BaseTool.prototype.applyToolAt = function(col, row, color, frame, overlay) {}; - ns.BaseTool.prototype.moveToolAt = function(col, row, frame) {}; + ns.BaseTool.prototype.moveToolAt = function(col, row, color, frame, overlay) {}; - ns.BaseTool.prototype.releaseToolAt = function(col, row, frame) {}; + ns.BaseTool.prototype.releaseToolAt = function(col, row, color, frame, overlay) {}; /** * Bresenham line algorihtm: Get an array of pixels from diff --git a/js/drawingtools/Eraser.js b/js/drawingtools/Eraser.js index e83cf617..064adf85 100644 --- a/js/drawingtools/Eraser.js +++ b/js/drawingtools/Eraser.js @@ -16,7 +16,7 @@ /** * @override */ - ns.Eraser.prototype.applyToolAt = function(col, row, color, drawer) { - this.superclass.applyToolAt.call(this, col, row, Constants.TRANSPARENT_COLOR, drawer); + ns.Eraser.prototype.applyToolAt = function(col, row, color, frame, overlay) { + this.superclass.applyToolAt.call(this, col, row, Constants.TRANSPARENT_COLOR, frame, overlay); }; })(); \ No newline at end of file diff --git a/js/drawingtools/Move.js b/js/drawingtools/Move.js index 23b0f58f..ec120287 100644 --- a/js/drawingtools/Move.js +++ b/js/drawingtools/Move.js @@ -19,16 +19,16 @@ /** * @override */ - ns.Move.prototype.applyToolAt = function(col, row, color, drawer) { + ns.Move.prototype.applyToolAt = function(col, row, color, frame, overlay) { this.startCol = col; this.startRow = row; - this.frameClone = drawer.frame.clone(); + this.frameClone = frame.clone(); }; - ns.Move.prototype.moveToolAt = function(col, row, color, drawer) { + ns.Move.prototype.moveToolAt = function(col, row, color, frame, overlay) { var colDiff = col - this.startCol, rowDiff = row - this.startRow; if (colDiff != 0 || rowDiff != 0) { - this.shiftFrame(colDiff, rowDiff, drawer.frame, this.frameClone); + this.shiftFrame(colDiff, rowDiff, frame, this.frameClone); } }; @@ -49,7 +49,7 @@ /** * @override */ - ns.Move.prototype.releaseToolAt = function(col, row, color, drawer) { - this.moveToolAt(col, row, color, drawer); + ns.Move.prototype.releaseToolAt = function(col, row, color, frame, overlay) { + this.moveToolAt(col, row, color, frame, overlay); }; })(); diff --git a/js/drawingtools/PaintBucket.js b/js/drawingtools/PaintBucket.js index f3c3ccfe..b3176324 100644 --- a/js/drawingtools/PaintBucket.js +++ b/js/drawingtools/PaintBucket.js @@ -15,12 +15,11 @@ /** * @override */ - ns.PaintBucket.prototype.applyToolAt = function(col, row, color, drawer) { + ns.PaintBucket.prototype.applyToolAt = function(col, row, color, frame, overlay) { // Change model: - var targetColor = drawer.frame.getPixel(col, row); - //this.recursiveFloodFill_(frame, col, row, targetColor, color); - this.queueLinearFloodFill_(drawer.frame, col, row, targetColor, color); + var targetColor = frame.getPixel(col, row); + this.queueLinearFloodFill_(frame, col, row, targetColor, color); }; /** diff --git a/js/drawingtools/Rectangle.js b/js/drawingtools/Rectangle.js index 5f2fa7c7..f5fd906d 100644 --- a/js/drawingtools/Rectangle.js +++ b/js/drawingtools/Rectangle.js @@ -19,17 +19,16 @@ /** * @override */ - ns.Rectangle.prototype.applyToolAt = function(col, row, color, drawer) { + ns.Rectangle.prototype.applyToolAt = function(col, row, color, frame, overlay) { this.startCol = col; this.startRow = row; // Drawing the first point of the rectangle in the fake overlay canvas: - drawer.overlayFrame.setPixel(col, row, color); + overlay.setPixel(col, row, color); }; - ns.Rectangle.prototype.moveToolAt = function(col, row, color, drawer) { - // Clean overlay canvas: - drawer.clearOverlay(); + ns.Rectangle.prototype.moveToolAt = function(col, row, color, frame, overlay) { + overlay.clear(); // When the user moussemove (before releasing), we dynamically compute the // pixel to draw the line and draw this line in the overlay : @@ -41,21 +40,21 @@ if(color == Constants.TRANSPARENT_COLOR) { color = Constants.SELECTION_TRANSPARENT_COLOR; } - drawer.overlayFrame.setPixel(strokePoints[i].col, strokePoints[i].row, color); + overlay.setPixel(strokePoints[i].col, strokePoints[i].row, color); } }; /** * @override */ - ns.Rectangle.prototype.releaseToolAt = function(col, row, color, drawer) { - drawer.clearOverlay(); + ns.Rectangle.prototype.releaseToolAt = function(col, row, color, frame, overlay) { + overlay.clear(); // If the stroke tool is released outside of the canvas, we cancel the stroke: - if(drawer.frame.containsPixel(col, row)) { + if(frame.containsPixel(col, row)) { var strokePoints = this.getRectanglePixels_(this.startCol, col, this.startRow, row); for(var i = 0; i< strokePoints.length; i++) { // Change model: - drawer.frame.setPixel(strokePoints[i].col, strokePoints[i].row, color); + frame.setPixel(strokePoints[i].col, strokePoints[i].row, color); } // The user released the tool to draw a line. We will compute the pixel coordinate, impact // the model and draw them in the drawing canvas (not the fake overlay anymore) diff --git a/js/drawingtools/SimplePen.js b/js/drawingtools/SimplePen.js index 86065d63..ffc0047f 100644 --- a/js/drawingtools/SimplePen.js +++ b/js/drawingtools/SimplePen.js @@ -18,27 +18,27 @@ /** * @override */ - ns.SimplePen.prototype.applyToolAt = function(col, row, color, drawer) { - if (drawer.frame.containsPixel(col, row)) { + ns.SimplePen.prototype.applyToolAt = function(col, row, color, frame, overlay) { + if (frame.containsPixel(col, row)) { this.previousCol = col; this.previousRow = row; - drawer.frame.setPixel(col, row, color); + frame.setPixel(col, row, color); } }; - ns.SimplePen.prototype.moveToolAt = function(col, row, color, drawer) { - + ns.SimplePen.prototype.moveToolAt = function(col, row, color, frame, overlay) { 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); for(var i=0, l=interpolatedPixels.length; i