From 9758aa62d9fea0c2848cae8cf738ce7ce95600f4 Mon Sep 17 00:00:00 2001 From: Vince Date: Sun, 2 Sep 2012 17:49:28 +0200 Subject: [PATCH] Add interpolation when using the pen tool quickly --- js/drawingtools/BaseTool.js | 37 ++++++++++++++++++++ js/drawingtools/SimplePen.js | 23 ++++++++++++- js/drawingtools/Stroke.js | 37 -------------------- js/piskel.js | 66 +++++++++++++++++++++--------------- 4 files changed, 98 insertions(+), 65 deletions(-) diff --git a/js/drawingtools/BaseTool.js b/js/drawingtools/BaseTool.js index 81a71884..e4a71678 100644 --- a/js/drawingtools/BaseTool.js +++ b/js/drawingtools/BaseTool.js @@ -55,4 +55,41 @@ ns.BaseTool.prototype.removeCanvasOverlays = function () { $(".canvas-overlay").remove(); }; + + /** + * Bresenham line algorihtm: Get an array of pixels from + * start and end coordinates. + * + * http://en.wikipedia.org/wiki/Bresenham's_line_algorithm + * http://stackoverflow.com/questions/4672279/bresenham-algorithm-in-javascript + * + * @private + */ + ns.BaseTool.prototype.getLinePixels_ = function(x0, x1, y0, y1) { + + var pixels = []; + var dx = Math.abs(x1-x0); + var dy = Math.abs(y1-y0); + var sx = (x0 < x1) ? 1 : -1; + var sy = (y0 < y1) ? 1 : -1; + var err = dx-dy; + + while(true){ + + // Do what you need to for this + pixels.push({"col": x0, "row": y0}); + + if ((x0==x1) && (y0==y1)) break; + var e2 = 2*err; + if (e2>-dy){ + err -= dy; + x0 += sx; + } + if (e2 < dx) { + err += dx; + y0 += sy; + } + } + return pixels; + }; })(); diff --git a/js/drawingtools/SimplePen.js b/js/drawingtools/SimplePen.js index ed8dac33..a7d4f45c 100644 --- a/js/drawingtools/SimplePen.js +++ b/js/drawingtools/SimplePen.js @@ -10,6 +10,9 @@ this.toolId = "tool-pen" }; + this.previousCol = null; + this.previousRow = null; + pskl.utils.inherit(ns.SimplePen, ns.BaseTool); /** @@ -17,6 +20,9 @@ */ ns.SimplePen.prototype.applyToolAt = function(col, row, frame, color, canvas, dpi) { + this.previousCol = col; + this.previousRow = row; + // Change model: var color = pskl.utils.normalizeColor(color); if (color != frame[col][row]) { @@ -29,6 +35,21 @@ }; ns.SimplePen.prototype.moveToolAt = function(col, row, frame, color, canvas, dpi) { - this.applyToolAt(col, row, frame, color, canvas, dpi); + + 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-dy){ - err -= dy; - x0 += sx; - } - if (e2 < dx) { - err += dx; - y0 += sy; - } - } - return pixels; - }; - })(); diff --git a/js/piskel.js b/js/piskel.js index 04152da4..f3310022 100644 --- a/js/piskel.js +++ b/js/piskel.js @@ -11,18 +11,23 @@ $.namespace("pskl"); DEFAULT_PEN_COLOR = '#000000', PISKEL_SERVICE_URL = 'http://2.piskel-app.appspot.com', + // Temporary zoom implementation to easily get bigger canvases to + // see how good perform critical algorithms on big canvas. + zoom = 1, + // Configuration: // Canvas size in pixel size (not dpi related) - framePixelWidth = 32, - framePixelHeight = 32, + framePixelWidth = 32 * zoom, + framePixelHeight = 32 * zoom, + // Scaling factors for a given frameSheet rendering: // Main drawing area: - drawingCanvasDpi = 20, + drawingCanvasDpi = Math.ceil(20/ zoom), // Canvas previous in the slideshow: - previewTileCanvasDpi = 4, + previewTileCanvasDpi = Math.ceil(4 / zoom), // Ainmated canvas preview: - previewAnimationCanvasDpi = 8, + previewAnimationCanvasDpi = Math.ceil(8 / zoom), // DOM references: drawingAreaContainer, @@ -39,6 +44,7 @@ $.namespace("pskl"); paletteColors = [], currentFrame = null; currentToolBehavior = null, + previousMousemoveTime = 0, //utility _normalizeColor = function (color) { @@ -131,21 +137,22 @@ $.namespace("pskl"); }, removeMessage : function () { - var message = $("user-message"); - if (message) { - message.parentNode.removeChild(message); + var message = $("#user-message"); + if (message.length) { + message.remove(); } }, persistToLocalStorageRequest: function() { // Persist to localStorage when drawing. We throttle localStorage accesses // for high frequency drawing (eg mousemove). - if(localStorageThrottler == null) { - localStorageThrottler = window.setTimeout(function() { - piskel.persistToLocalStorage(); - localStorageThrottler = null; - }, 1000); + if(localStorageThrottler != null) { + window.clearTimeout(localStorageThrottler); } + localStorageThrottler = window.setTimeout(function() { + piskel.persistToLocalStorage(); + localStorageThrottler = null; + }, 1000); }, persistToLocalStorage: function() { @@ -394,20 +401,25 @@ $.namespace("pskl"); onCanvasMousemove : function (event) { //this.updateCursorInfo(event); - if (isClicked) { - var spriteCoordinate = this.getSpriteCoordinate(event); - currentToolBehavior.moveToolAt( - spriteCoordinate.col, - spriteCoordinate.row, - currentFrame, - penColor, - drawingAreaCanvas, - drawingCanvasDpi); - - // 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. - piskel.persistToLocalStorageRequest(); + var currentTime = new Date().getTime(); + // Throttling of the mousemove event: + if ((currentTime - previousMousemoveTime) > 40 ) { + if (isClicked) { + var spriteCoordinate = this.getSpriteCoordinate(event); + currentToolBehavior.moveToolAt( + spriteCoordinate.col, + spriteCoordinate.row, + currentFrame, + penColor, + drawingAreaCanvas, + drawingCanvasDpi); + + // 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. + piskel.persistToLocalStorageRequest(); + } + previousMousemoveTime = currentTime; } },