diff --git a/Gruntfile.js b/Gruntfile.js index 07680eff..cd453635 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -10,16 +10,24 @@ * If you run this task locally, it may require some env set up first. */ -module.exports = function (grunt) { +module.exports = function(grunt) { grunt.initConfig({ - jshint : { + jshint: { /*options: { - "evil": true, - "asi": true, - "smarttabs": true, - "eqnull": true - },*/ - files : [ + "evil": true, + "asi": true, + "smarttabs": true, + "eqnull": true + },*/ + options: { + indent:2, + undef : true, + latedef : true, + browser : true, + jquery : true, + globals : {'pskl':true, 'Events':true, 'Constants':true, 'console' : true, 'module':true, 'require':true} + }, + files: [ 'Gruntfile.js', 'package.json', 'js/**/*.js', @@ -69,13 +77,23 @@ module.exports = function (grunt) { } }); + grunt.config.set('leadingIndent.indentation', 'spaces'); + grunt.config.set('leadingIndent.jsFiles', { + src: ['js/**/*.js','!js/lib/**/*.js'] + }); + grunt.config.set('leadingIndent.cssFiles', { + src: ['css/**/*.css'] + }); + grunt.loadNpmTasks('grunt-contrib-connect'); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-concat'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-ghost'); + grunt.loadNpmTasks('grunt-leading-indent'); + + grunt.registerTask('lint', ['leadingIndent:jsFiles', 'leadingIndent:cssFiles', 'jshint']); + grunt.registerTask('test', ['leadingIndent:jsFiles', 'leadingIndent:cssFiles', 'jshint', 'connect', 'ghost']); grunt.registerTask('default', ['jshint', 'concat', 'uglify']); - grunt.registerTask('lint', ['jshint']); - grunt.registerTask('test', ['jshint', 'connect', 'ghost']); }; diff --git a/js/Constants.js b/js/Constants.js index c7abf94a..5b333e5c 100644 --- a/js/Constants.js +++ b/js/Constants.js @@ -1,36 +1,38 @@ // TODO(grosbouddha): put under pskl namespace. var Constants = { - DEFAULT_SIZE : { - height : 32, - width : 32 - }, + DEFAULT_SIZE : { + height : 32, + width : 32 + }, - MAX_HEIGHT : 128, - MAX_WIDTH : 128, + MAX_HEIGHT : 128, + MAX_WIDTH : 128, - DEFAULT_PEN_COLOR : '#000000', - TRANSPARENT_COLOR : 'TRANSPARENT', - - /* - * Fake semi-transparent color used to highlight transparent - * strokes and rectangles: - */ - SELECTION_TRANSPARENT_COLOR: 'rgba(255, 255, 255, 0.6)', + PREVIEW_FILM_SIZE : 120, - /* - * When a tool is hovering the drawing canvas, we highlight the eventual - * pixel target with this color: - */ - TOOL_TARGET_HIGHLIGHT_COLOR: 'rgba(255, 255, 255, 0.2)', - - /* - * Default entry point for piskel web service: - */ - PISKEL_SERVICE_URL: 'http://3.piskel-app.appspot.com', + DEFAULT_PEN_COLOR : '#000000', + TRANSPARENT_COLOR : 'TRANSPARENT', + + /* + * Fake semi-transparent color used to highlight transparent + * strokes and rectangles: + */ + SELECTION_TRANSPARENT_COLOR: 'rgba(255, 255, 255, 0.6)', - GRID_STROKE_WIDTH: 1, - GRID_STROKE_COLOR: "lightgray", + /* + * When a tool is hovering the drawing canvas, we highlight the eventual + * pixel target with this color: + */ + TOOL_TARGET_HIGHLIGHT_COLOR: 'rgba(255, 255, 255, 0.2)', + + /* + * Default entry point for piskel web service: + */ + PISKEL_SERVICE_URL: 'http://3.piskel-app.appspot.com', - LEFT_BUTTON : "left_button_1", - RIGHT_BUTTON : "right_button_2" + GRID_STROKE_WIDTH: 1, + GRID_STROKE_COLOR: "lightgray", + + LEFT_BUTTON : "left_button_1", + RIGHT_BUTTON : "right_button_2" }; \ No newline at end of file diff --git a/js/Events.js b/js/Events.js index 34283d0d..06e2dda2 100644 --- a/js/Events.js +++ b/js/Events.js @@ -1,64 +1,64 @@ // TODO(grosbouddha): put under pskl namespace. Events = { - - TOOL_SELECTED : "TOOL_SELECTED", - TOOL_RELEASED : "TOOL_RELEASED", - PRIMARY_COLOR_SELECTED: "PRIMARY_COLOR_SELECTED", - PRIMARY_COLOR_UPDATED: "PRIMARY_COLOR_UPDATED", - SECONDARY_COLOR_SELECTED: "SECONDARY_COLOR_SELECTED", - SECONDARY_COLOR_UPDATED: "SECONDARY_COLOR_UPDATED", + + TOOL_SELECTED : "TOOL_SELECTED", + TOOL_RELEASED : "TOOL_RELEASED", + PRIMARY_COLOR_SELECTED: "PRIMARY_COLOR_SELECTED", + PRIMARY_COLOR_UPDATED: "PRIMARY_COLOR_UPDATED", + SECONDARY_COLOR_SELECTED: "SECONDARY_COLOR_SELECTED", + SECONDARY_COLOR_UPDATED: "SECONDARY_COLOR_UPDATED", - /** - * When this event is emitted, a request is sent to the localstorage - * Service to save the current framesheet. The storage service - * may not immediately store data (internal throttling of requests). - */ - LOCALSTORAGE_REQUEST: "LOCALSTORAGE_REQUEST", + /** + * When this event is emitted, a request is sent to the localstorage + * Service to save the current framesheet. The storage service + * may not immediately store data (internal throttling of requests). + */ + LOCALSTORAGE_REQUEST: "LOCALSTORAGE_REQUEST", - CANVAS_RIGHT_CLICKED: "CANVAS_RIGHT_CLICKED", + CANVAS_RIGHT_CLICKED: "CANVAS_RIGHT_CLICKED", - /** - * Event to request a refresh of the display. - * A bit overkill but, it's just workaround in our current drawing system. - * TODO: Remove or rework when redraw system is refactored. - */ - REFRESH: "REFRESH", + /** + * Event to request a refresh of the display. + * A bit overkill but, it's just workaround in our current drawing system. + * TODO: Remove or rework when redraw system is refactored. + */ + REFRESH: "REFRESH", - /** - * Temporary event to bind the redraw of right preview film to the canvas. - * This redraw should be driven by model updates. - * TODO(vincz): Remove. - */ - REDRAW_PREVIEWFILM: "REDRAW_PREVIEWFILM", + /** + * Temporary event to bind the redraw of right preview film to the canvas. + * This redraw should be driven by model updates. + * TODO(vincz): Remove. + */ + REDRAW_PREVIEWFILM: "REDRAW_PREVIEWFILM", - /** - * Fired each time a user setting change. - * The payload will be: - * 1st argument: Name of the settings - * 2nd argument: New value - */ - USER_SETTINGS_CHANGED: "USER_SETTINGS_CHANGED", - - /** - * The framesheet was reseted and is now probably drastically different. - * Number of frames, content of frames, color used for the palette may have changed. - */ - FRAMESHEET_RESET: "FRAMESHEET_RESET", + /** + * Fired each time a user setting change. + * The payload will be: + * 1st argument: Name of the settings + * 2nd argument: New value + */ + USER_SETTINGS_CHANGED: "USER_SETTINGS_CHANGED", + + /** + * The framesheet was reseted and is now probably drastically different. + * Number of frames, content of frames, color used for the palette may have changed. + */ + FRAMESHEET_RESET: "FRAMESHEET_RESET", - FRAME_SIZE_CHANGED : "FRAME_SIZE_CHANGED", + FRAME_SIZE_CHANGED : "FRAME_SIZE_CHANGED", - CURRENT_FRAME_SET: "CURRENT_FRAME_SET", + CURRENT_FRAME_SET: "CURRENT_FRAME_SET", - SELECTION_CREATED: "SELECTION_CREATED", - SELECTION_MOVE_REQUEST: "SELECTION_MOVE_REQUEST", - SELECTION_DISMISSED: "SELECTION_DISMISSED", - - SHOW_NOTIFICATION: "SHOW_NOTIFICATION", - HIDE_NOTIFICATION: "HIDE_NOTIFICATION", + SELECTION_CREATED: "SELECTION_CREATED", + SELECTION_MOVE_REQUEST: "SELECTION_MOVE_REQUEST", + SELECTION_DISMISSED: "SELECTION_DISMISSED", + + SHOW_NOTIFICATION: "SHOW_NOTIFICATION", + HIDE_NOTIFICATION: "HIDE_NOTIFICATION", - UNDO: "UNDO", - REDO: "REDO", - CUT: "CUT", - COPY: "COPY", - PASTE: "PASTE" + UNDO: "UNDO", + REDO: "REDO", + CUT: "CUT", + COPY: "COPY", + PASTE: "PASTE" }; \ No newline at end of file diff --git a/js/controller/AnimatedPreviewController.js b/js/controller/AnimatedPreviewController.js index bcec77e3..9b50961a 100644 --- a/js/controller/AnimatedPreviewController.js +++ b/js/controller/AnimatedPreviewController.js @@ -1,67 +1,67 @@ (function () { - var ns = $.namespace("pskl.controller"); - ns.AnimatedPreviewController = function (framesheet, container, dpi) { - this.framesheet = framesheet; - this.container = container; + var ns = $.namespace("pskl.controller"); + ns.AnimatedPreviewController = function (framesheet, container, dpi) { + this.framesheet = framesheet; + this.container = container; - this.elapsedTime = 0; + this.elapsedTime = 0; + this.currentIndex = 0; + + this.fps = parseInt($("#preview-fps")[0].value, 10); + + var renderingOptions = { + "dpi": this.calculateDPI_() + }; + this.renderer = new pskl.rendering.FrameRenderer(this.container, renderingOptions); + + $.subscribe(Events.FRAME_SIZE_CHANGED, this.updateDPI_.bind(this)); + }; + + ns.AnimatedPreviewController.prototype.init = function () { + // the oninput event won't work on IE10 unfortunately, but at least will provide a + // consistent behavior across all other browsers that support the input type range + // see https://bugzilla.mozilla.org/show_bug.cgi?id=853670 + $("#preview-fps")[0].addEventListener('change', this.onFPSSliderChange.bind(this)); + }; + + ns.AnimatedPreviewController.prototype.onFPSSliderChange = function (evt) { + this.setFPS(parseInt($("#preview-fps")[0].value, 10)); + }; + + ns.AnimatedPreviewController.prototype.setFPS = function (fps) { + this.fps = fps; + $("#preview-fps").val(this.fps); + $("#display-fps").html(this.fps + " FPS"); + }; + + ns.AnimatedPreviewController.prototype.render = function (delta) { + this.elapsedTime += delta; + var index = Math.floor(this.elapsedTime / (1000/this.fps)); + if (index != this.currentIndex) { + this.currentIndex = index; + if (!this.framesheet.hasFrameAtIndex(this.currentIndex)) { this.currentIndex = 0; + this.elapsedTime = 0; + } + this.renderer.render(this.framesheet.getFrameByIndex(this.currentIndex)); + } + }; - this.fps = parseInt($("#preview-fps")[0].value, 10); - - var renderingOptions = { - "dpi": this.calculateDPI_() - }; - this.renderer = new pskl.rendering.FrameRenderer(this.container, renderingOptions); + /** + * Calculate the preview DPI depending on the framesheet size + */ + ns.AnimatedPreviewController.prototype.calculateDPI_ = function () { + var previewSize = 200, + framePixelHeight = this.framesheet.getCurrentFrame().getHeight(), + framePixelWidth = this.framesheet.getCurrentFrame().getWidth(); + // TODO (julz) : should have a utility to get a Size from framesheet easily (what about empty framesheets though ?) + + //return pskl.PixelUtils.calculateDPIForContainer($(".preview-container"), framePixelHeight, framePixelWidth); + return pskl.PixelUtils.calculateDPI(previewSize, previewSize, framePixelHeight, framePixelWidth); + }; - $.subscribe(Events.FRAME_SIZE_CHANGED, this.updateDPI_.bind(this)); - }; - - ns.AnimatedPreviewController.prototype.init = function () { - // the oninput event won't work on IE10 unfortunately, but at least will provide a - // consistent behavior across all other browsers that support the input type range - // see https://bugzilla.mozilla.org/show_bug.cgi?id=853670 - $("#preview-fps")[0].addEventListener('change', this.onFPSSliderChange.bind(this)); - }; - - ns.AnimatedPreviewController.prototype.onFPSSliderChange = function (evt) { - this.setFPS(parseInt($("#preview-fps")[0].value, 10)); - }; - - ns.AnimatedPreviewController.prototype.setFPS = function (fps) { - this.fps = fps; - $("#preview-fps").val(this.fps); - $("#display-fps").html(this.fps + " FPS"); - }; - - ns.AnimatedPreviewController.prototype.render = function (delta) { - this.elapsedTime += delta; - var index = Math.floor(this.elapsedTime / (1000/this.fps)); - if (index != this.currentIndex) { - this.currentIndex = index; - if (!this.framesheet.hasFrameAtIndex(this.currentIndex)) { - this.currentIndex = 0; - this.elapsedTime = 0; - } - this.renderer.render(this.framesheet.getFrameByIndex(this.currentIndex)); - } - }; - - /** - * Calculate the preview DPI depending on the framesheet size - */ - ns.AnimatedPreviewController.prototype.calculateDPI_ = function () { - var previewSize = 200, - framePixelHeight = this.framesheet.getCurrentFrame().getHeight(), - framePixelWidth = this.framesheet.getCurrentFrame().getWidth(); - // TODO (julz) : should have a utility to get a Size from framesheet easily (what about empty framesheets though ?) - - //return pskl.PixelUtils.calculateDPIForContainer($(".preview-container"), framePixelHeight, framePixelWidth); - return pskl.PixelUtils.calculateDPI(previewSize, previewSize, framePixelHeight, framePixelWidth); - }; - - ns.AnimatedPreviewController.prototype.updateDPI_ = function () { - this.dpi = this.calculateDPI_(); - this.renderer.updateDPI(this.dpi); - }; + ns.AnimatedPreviewController.prototype.updateDPI_ = function () { + this.dpi = this.calculateDPI_(); + this.renderer.updateDPI(this.dpi); + }; })(); \ No newline at end of file diff --git a/js/controller/DrawingController.js b/js/controller/DrawingController.js index 76201a11..fdedeb2c 100644 --- a/js/controller/DrawingController.js +++ b/js/controller/DrawingController.js @@ -25,8 +25,6 @@ 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(framesheet.getCurrentFrame()); - this.overlayRenderer.init(this.overlayFrame); // State of drawing controller: this.isClicked = false; @@ -35,12 +33,18 @@ this.currentToolBehavior = null; this.primaryColor = Constants.DEFAULT_PEN_COLOR; this.secondaryColor = Constants.TRANSPARENT_COLOR; + }; + ns.DrawingController.prototype.init = function () { + this.renderer.render(this.framesheet.getCurrentFrame()); + this.overlayRenderer.render(this.overlayFrame); + this.initMouseBehavior(); $.subscribe(Events.TOOL_SELECTED, $.proxy(function(evt, toolBehavior) { console.log("Tool selected: ", toolBehavior); this.currentToolBehavior = toolBehavior; + this.overlayFrame.clear(); }, this)); /** @@ -68,21 +72,21 @@ ns.DrawingController.prototype.initMouseBehavior = function() { var body = $('body'); - this.container.mousedown($.proxy(this.onMousedown_, this)); - this.container.mousemove($.proxy(this.onMousemove_, this)); - body.mouseup($.proxy(this.onMouseup_, this)); - - // Deactivate right click: - body.contextmenu(this.onCanvasContextMenu_); + this.container.mousedown($.proxy(this.onMousedown_, this)); + this.container.mousemove($.proxy(this.onMousemove_, this)); + body.mouseup($.proxy(this.onMouseup_, this)); + + // Deactivate right click: + body.contextmenu(this.onCanvasContextMenu_); }; ns.DrawingController.prototype.startDPIUpdateTimer_ = function () { - if (this.dpiUpdateTimer) { - window.clearInterval(this.dpiUpdateTimer); - } - this.dpiUpdateTimer = window.setTimeout($.proxy(this.updateDPI_, this), 200); + if (this.dpiUpdateTimer) { + window.clearInterval(this.dpiUpdateTimer); + } + this.dpiUpdateTimer = window.setTimeout($.proxy(this.updateDPI_, this), 200); }, /** @@ -98,144 +102,144 @@ * @private */ ns.DrawingController.prototype.onMousedown_ = function (event) { - this.isClicked = true; - - if(event.button == 2) { // right click - this.isRightClicked = true; - $.publish(Events.CANVAS_RIGHT_CLICKED); - } + this.isClicked = true; + + if(event.button == 2) { // right click + this.isRightClicked = true; + $.publish(Events.CANVAS_RIGHT_CLICKED); + } - var coords = this.getSpriteCoordinates(event); + var coords = this.getSpriteCoordinates(event); + + this.currentToolBehavior.applyToolAt( + coords.col, coords.row, + this.getCurrentColor_(), + this.framesheet.getCurrentFrame(), + this.overlayFrame, + this.wrapEvtInfo_(event) + ); - this.currentToolBehavior.applyToolAt( - coords.col, coords.row, - this.getCurrentColor_(), - this.framesheet.getCurrentFrame(), - this.overlayFrame, - this.wrapEvtInfo_(event) - ); - - $.publish(Events.LOCALSTORAGE_REQUEST); - }; + $.publish(Events.LOCALSTORAGE_REQUEST); + }; - /** + /** * @private */ - ns.DrawingController.prototype.onMousemove_ = function (event) { - var currentTime = new Date().getTime(); - // Throttling of the mousemove event: - if ((currentTime - this.previousMousemoveTime) > 40 ) { - var coords = this.getSpriteCoordinates(event); - if (this.isClicked) { - - this.currentToolBehavior.moveToolAt( - coords.col, coords.row, - this.getCurrentColor_(), - this.framesheet.getCurrentFrame(), - this.overlayFrame, - this.wrapEvtInfo_(event) - ); - - // 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 { - - this.currentToolBehavior.moveUnactiveToolAt( - coords.col, coords.row, - this.getCurrentColor_(), - this.framesheet.getCurrentFrame(), - this.overlayFrame, - this.wrapEvtInfo_(event) - ); - } - this.previousMousemoveTime = currentTime; - } - }; - - /** - * @private - */ - ns.DrawingController.prototype.onMouseup_ = function (event) { - if(this.isClicked || this.isRightClicked) { - // A mouse button was clicked on the drawing canvas before this mouseup event, - // the user was probably drawing on the canvas. - // Note: The mousemove movement (and the mouseup) may end up outside - // of the drawing canvas. - - this.isClicked = false; - this.isRightClicked = false; - - var coords = this.getSpriteCoordinates(event); - //console.log("mousemove: col: " + spriteCoordinate.col + " - row: " + spriteCoordinate.row); - this.currentToolBehavior.releaseToolAt( + ns.DrawingController.prototype.onMousemove_ = function (event) { + var currentTime = new Date().getTime(); + // Throttling of the mousemove event: + if ((currentTime - this.previousMousemoveTime) > 40 ) { + var coords = this.getSpriteCoordinates(event); + if (this.isClicked) { + + this.currentToolBehavior.moveToolAt( coords.col, coords.row, this.getCurrentColor_(), this.framesheet.getCurrentFrame(), this.overlayFrame, this.wrapEvtInfo_(event) ); - - $.publish(Events.TOOL_RELEASED); - } - }, - - /** - * @private - */ - ns.DrawingController.prototype.wrapEvtInfo_ = function (event) { - var evtInfo = {}; - if (event.button === 0) { - evtInfo.button = Constants.LEFT_BUTTON; - } else if (event.button == 2) { - evtInfo.button = Constants.RIGHT_BUTTON; - } - return evtInfo; - }, - - /** - * @private - */ - ns.DrawingController.prototype.getRelativeCoordinates = function (clientX, clientY) { - var canvasPageOffset = this.container.offset(); - return { - x : clientX - canvasPageOffset.left, - y : clientY - canvasPageOffset.top - }; - }; - - /** - * @private - */ - ns.DrawingController.prototype.getSpriteCoordinates = function(event) { - var coords = this.getRelativeCoordinates(event.clientX, event.clientY); - return this.renderer.convertPixelCoordinatesIntoSpriteCoordinate(coords); - }; - - /** - * @private - */ - ns.DrawingController.prototype.getCurrentColor_ = function () { - if(this.isRightClicked) { - return this.secondaryColor; + + // 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 { - return this.primaryColor; - } - }; - /** - * @private - */ - ns.DrawingController.prototype.onCanvasContextMenu_ = function (event) { - if ($(event.target).closest('#drawing-canvas-container').length) { - // Deactivate right click on drawing canvas only. - event.preventDefault(); - event.stopPropagation(); - event.cancelBubble = true; - return false; - } + this.currentToolBehavior.moveUnactiveToolAt( + coords.col, coords.row, + this.getCurrentColor_(), + this.framesheet.getCurrentFrame(), + this.overlayFrame, + this.wrapEvtInfo_(event) + ); + } + this.previousMousemoveTime = currentTime; + } + }; + + /** + * @private + */ + ns.DrawingController.prototype.onMouseup_ = function (event) { + if(this.isClicked || this.isRightClicked) { + // A mouse button was clicked on the drawing canvas before this mouseup event, + // the user was probably drawing on the canvas. + // Note: The mousemove movement (and the mouseup) may end up outside + // of the drawing canvas. + + this.isClicked = false; + this.isRightClicked = false; + + var coords = this.getSpriteCoordinates(event); + //console.log("mousemove: col: " + spriteCoordinate.col + " - row: " + spriteCoordinate.row); + this.currentToolBehavior.releaseToolAt( + coords.col, coords.row, + this.getCurrentColor_(), + this.framesheet.getCurrentFrame(), + this.overlayFrame, + this.wrapEvtInfo_(event) + ); + + $.publish(Events.TOOL_RELEASED); + } + }; + + /** + * @private + */ + ns.DrawingController.prototype.wrapEvtInfo_ = function (event) { + var evtInfo = {}; + if (event.button === 0) { + evtInfo.button = Constants.LEFT_BUTTON; + } else if (event.button == 2) { + evtInfo.button = Constants.RIGHT_BUTTON; + } + return evtInfo; + }; + + /** + * @private + */ + ns.DrawingController.prototype.getRelativeCoordinates = function (clientX, clientY) { + var canvasPageOffset = this.container.offset(); + return { + x : clientX - canvasPageOffset.left, + y : clientY - canvasPageOffset.top }; + }; + + /** + * @private + */ + ns.DrawingController.prototype.getSpriteCoordinates = function(event) { + var coords = this.getRelativeCoordinates(event.clientX, event.clientY); + return this.renderer.convertPixelCoordinatesIntoSpriteCoordinate(coords); + }; + + /** + * @private + */ + ns.DrawingController.prototype.getCurrentColor_ = function () { + if(this.isRightClicked) { + return this.secondaryColor; + } else { + return this.primaryColor; + } + }; + + /** + * @private + */ + ns.DrawingController.prototype.onCanvasContextMenu_ = function (event) { + if ($(event.target).closest('#drawing-canvas-container').length) { + // Deactivate right click on drawing canvas only. + event.preventDefault(); + event.stopPropagation(); + event.cancelBubble = true; + return false; + } + }; ns.DrawingController.prototype.render = function () { this.renderFrame(); @@ -271,11 +275,11 @@ */ ns.DrawingController.prototype.calculateDPI_ = function() { var availableViewportHeight = $('#main-wrapper').height(), - leftSectionWidth = $('.left-column').outerWidth(true), - rightSectionWidth = $('.right-column').outerWidth(true), - availableViewportWidth = $('#main-wrapper').width() - leftSectionWidth - rightSectionWidth, - framePixelHeight = this.framesheet.getCurrentFrame().getHeight(), - framePixelWidth = this.framesheet.getCurrentFrame().getWidth(); + leftSectionWidth = $('.left-column').outerWidth(true), + rightSectionWidth = $('.right-column').outerWidth(true), + availableViewportWidth = $('#main-wrapper').width() - leftSectionWidth - rightSectionWidth, + framePixelHeight = this.framesheet.getCurrentFrame().getHeight(), + framePixelWidth = this.framesheet.getCurrentFrame().getWidth(); if (pskl.UserSettings.get(pskl.UserSettings.SHOW_GRID)) { availableViewportWidth = availableViewportWidth - (framePixelWidth * Constants.GRID_STROKE_WIDTH); @@ -299,7 +303,7 @@ var currentFrameHeight = this.framesheet.getCurrentFrame().getHeight(); var canvasHeight = currentFrameHeight * dpi; if (pskl.UserSettings.get(pskl.UserSettings.SHOW_GRID)) { - canvasHeight += Constants.GRID_STROKE_WIDTH * currentFrameHeight; + canvasHeight += Constants.GRID_STROKE_WIDTH * currentFrameHeight; } var verticalGapInPixel = Math.floor(($('#main-wrapper').height() - canvasHeight) / 2); diff --git a/js/controller/PreviewFilmController.js b/js/controller/PreviewFilmController.js index 456f6c9b..b4e6f0f5 100644 --- a/js/controller/PreviewFilmController.js +++ b/js/controller/PreviewFilmController.js @@ -1,217 +1,220 @@ (function () { - var ns = $.namespace("pskl.controller"); - ns.PreviewFilmController = function (framesheet, container, dpi) { + var ns = $.namespace("pskl.controller"); + ns.PreviewFilmController = function (framesheet, container, dpi) { - this.framesheet = framesheet; - this.container = container; - this.dpi = this.calculateDPI_(); + this.framesheet = framesheet; + this.container = container; + this.dpi = this.calculateDPI_(); - this.redrawFlag = true; + this.redrawFlag = true; + }; - $.subscribe(Events.TOOL_RELEASED, this.flagForRedraw_.bind(this)); - $.subscribe(Events.FRAMESHEET_RESET, this.flagForRedraw_.bind(this)); - $.subscribe(Events.FRAMESHEET_RESET, this.refreshDPI_.bind(this)); + ns.PreviewFilmController.prototype.init = function() { + $.subscribe(Events.TOOL_RELEASED, this.flagForRedraw_.bind(this)); + $.subscribe(Events.FRAMESHEET_RESET, this.flagForRedraw_.bind(this)); + $.subscribe(Events.FRAMESHEET_RESET, this.refreshDPI_.bind(this)); - $('#preview-list-scroller').scroll(this.updateScrollerOverflows.bind(this)); - this.updateScrollerOverflows(); - }; + $('#preview-list-scroller').scroll(this.updateScrollerOverflows.bind(this)); + this.updateScrollerOverflows(); + }; - ns.PreviewFilmController.prototype.init = function() {}; + ns.PreviewFilmController.prototype.addFrame = function () { + this.framesheet.addEmptyFrame(); + this.framesheet.setCurrentFrameIndex(this.framesheet.getFrameCount() - 1); + this.updateScrollerOverflows(); + }; - ns.PreviewFilmController.prototype.addFrame = function () { - this.framesheet.addEmptyFrame(); - this.framesheet.setCurrentFrameIndex(this.framesheet.getFrameCount() - 1); - this.updateScrollerOverflows(); - }; + ns.PreviewFilmController.prototype.flagForRedraw_ = function () { + this.redrawFlag = true; + }; - ns.PreviewFilmController.prototype.flagForRedraw_ = function () { - this.redrawFlag = true; - }; + ns.PreviewFilmController.prototype.refreshDPI_ = function () { + this.dpi = this.calculateDPI_(); + }; - ns.PreviewFilmController.prototype.refreshDPI_ = function () { - this.dpi = this.calculateDPI_(); - }; + ns.PreviewFilmController.prototype.render = function () { + if (this.redrawFlag) { + // TODO(vincz): Full redraw on any drawing modification, optimize. + this.createPreviews_(); + this.redrawFlag = false; + } + }; - ns.PreviewFilmController.prototype.render = function () { - if (this.redrawFlag) { - // TODO(vincz): Full redraw on any drawing modification, optimize. - this.createPreviews_(); - this.redrawFlag = false; - } - }; + ns.PreviewFilmController.prototype.updateScrollerOverflows = function () { + var scroller = $('#preview-list-scroller'); + var scrollerHeight = scroller.height(); + var scrollTop = scroller.scrollTop(); + var scrollerContentHeight = $('#preview-list').height(); + var treshold = $('.top-overflow').height(); + var overflowTop = false, + overflowBottom = false; + if (scrollerHeight < scrollerContentHeight) { + if (scrollTop > treshold) { + overflowTop = true; + } + var scrollBottom = (scrollerContentHeight - scrollTop) - scrollerHeight; + if (scrollBottom > treshold) { + overflowBottom = true; + } + } + var wrapper = $('#preview-list-wrapper'); + wrapper.toggleClass('top-overflow-visible', overflowTop); + wrapper.toggleClass('bottom-overflow-visible', overflowBottom); + }; - ns.PreviewFilmController.prototype.updateScrollerOverflows = function () { - var scroller = $('#preview-list-scroller'); - var scrollerHeight = scroller.height(); - var scrollTop = scroller.scrollTop(); - var scrollerContentHeight = $('#preview-list').height(); - var treshold = $('.top-overflow').height(); - var overflowTop = false, - overflowBottom = false; - if (scrollerHeight < scrollerContentHeight) { - if (scrollTop > treshold) { - overflowTop = true; - } - var scrollBottom = (scrollerContentHeight - scrollTop) - scrollerHeight; - if (scrollBottom > treshold) { - overflowBottom = true; - } - } - var wrapper = $('#preview-list-wrapper'); - wrapper.toggleClass('top-overflow-visible', overflowTop); - wrapper.toggleClass('bottom-overflow-visible', overflowBottom); - }; + ns.PreviewFilmController.prototype.createPreviews_ = function () { + + this.container.html(""); + // Manually remove tooltips since mouseout events were shortcut by the DOM refresh: + $(".tooltip").remove(); - ns.PreviewFilmController.prototype.createPreviews_ = function () { - - this.container.html(""); - // Manually remove tooltips since mouseout events were shortcut by the DOM refresh: - $(".tooltip").remove(); + var frameCount = this.framesheet.getFrameCount(); - var frameCount = this.framesheet.getFrameCount(); + for (var i = 0, l = frameCount; i < l ; i++) { + this.container.append(this.createPreviewTile_(i)); + } + // Append 'new empty frame' button + var newFrameButton = document.createElement("div"); + newFrameButton.id = "add-frame-action"; + newFrameButton.className = "add-frame-action"; + newFrameButton.innerHTML = "

Add new frame

"; + this.container.append(newFrameButton); - for (var i = 0, l = frameCount; i < l ; i++) { - this.container.append(this.createPreviewTile_(i)); - } - // Append 'new empty frame' button - var newFrameButton = document.createElement("div"); - newFrameButton.id = "add-frame-action"; - newFrameButton.className = "add-frame-action"; - newFrameButton.innerHTML = "

Add new frame

"; - this.container.append(newFrameButton); + $(newFrameButton).click(this.addFrame.bind(this)); - $(newFrameButton).click(this.addFrame.bind(this)); - - var needDragndropBehavior = (frameCount > 1); - if(needDragndropBehavior) { - this.initDragndropBehavior_(); - } - this.updateScrollerOverflows(); - }; + var needDragndropBehavior = (frameCount > 1); + if(needDragndropBehavior) { + this.initDragndropBehavior_(); + } + this.updateScrollerOverflows(); + }; - /** - * @private - */ - ns.PreviewFilmController.prototype.initDragndropBehavior_ = function () { - - $("#preview-list").sortable({ - placeholder: "preview-tile-drop-proxy", - update: $.proxy(this.onUpdate_, this), - items: ".preview-tile" - }); - $("#preview-list").disableSelection(); - }; + /** + * @private + */ + ns.PreviewFilmController.prototype.initDragndropBehavior_ = function () { + + $("#preview-list").sortable({ + placeholder: "preview-tile-drop-proxy", + update: $.proxy(this.onUpdate_, this), + items: ".preview-tile" + }); + $("#preview-list").disableSelection(); + }; - /** - * @private - */ - ns.PreviewFilmController.prototype.onUpdate_ = function( event, ui ) { - var originFrameId = parseInt(ui.item.data("tile-number"), 10); - var targetInsertionId = $('.preview-tile').index(ui.item); + /** + * @private + */ + ns.PreviewFilmController.prototype.onUpdate_ = function( event, ui ) { + var originFrameId = parseInt(ui.item.data("tile-number"), 10); + var targetInsertionId = $('.preview-tile').index(ui.item); - this.framesheet.moveFrame(originFrameId, targetInsertionId); - this.framesheet.setCurrentFrameIndex(targetInsertionId); + this.framesheet.moveFrame(originFrameId, targetInsertionId); + this.framesheet.setCurrentFrameIndex(targetInsertionId); - // TODO(grosbouddha): move localstorage request to the model layer? - $.publish(Events.LOCALSTORAGE_REQUEST); - }; + // TODO(grosbouddha): 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) { - var currentFrame = this.framesheet.getFrameByIndex(tileNumber); - - var previewTileRoot = document.createElement("li"); - var classname = "preview-tile"; - previewTileRoot.setAttribute("data-tile-number", tileNumber); + /** + * @private + * TODO(vincz): clean this giant rendering function & remove listeners. + */ + 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 (this.framesheet.getCurrentFrame() == currentFrame) { - classname += " selected"; - } - previewTileRoot.className = classname; + if (this.framesheet.getCurrentFrame() == currentFrame) { + classname += " selected"; + } + previewTileRoot.className = classname; - var canvasContainer = document.createElement("div"); - canvasContainer.className = "canvas-container"; - - var canvasBackground = document.createElement("div"); - canvasBackground.className = "canvas-background"; - canvasContainer.appendChild(canvasBackground); - - previewTileRoot.addEventListener('click', this.onPreviewClick_.bind(this, tileNumber)); + var canvasContainer = document.createElement("div"); + canvasContainer.className = "canvas-container"; + + var canvasBackground = document.createElement("div"); + canvasBackground.className = "canvas-background"; + canvasContainer.appendChild(canvasBackground); + + previewTileRoot.addEventListener('click', this.onPreviewClick_.bind(this, tileNumber)); - var cloneFrameButton = document.createElement("button"); - cloneFrameButton.setAttribute('rel', 'tooltip'); - cloneFrameButton.setAttribute('data-placement', 'right'); - cloneFrameButton.setAttribute('title', 'Duplicate this frame'); - cloneFrameButton.className = "tile-overlay duplicate-frame-action"; - previewTileRoot.appendChild(cloneFrameButton); - cloneFrameButton.addEventListener('click', this.onAddButtonClick_.bind(this, tileNumber)); + var cloneFrameButton = document.createElement("button"); + cloneFrameButton.setAttribute('rel', 'tooltip'); + cloneFrameButton.setAttribute('data-placement', 'right'); + cloneFrameButton.setAttribute('title', 'Duplicate this frame'); + cloneFrameButton.className = "tile-overlay duplicate-frame-action"; + previewTileRoot.appendChild(cloneFrameButton); + cloneFrameButton.addEventListener('click', this.onAddButtonClick_.bind(this, tileNumber)); - // 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 }; - var currentFrameRenderer = new pskl.rendering.FrameRenderer($(canvasContainer), renderingOptions, "tile-view"); - currentFrameRenderer.init(currentFrame); - - previewTileRoot.appendChild(canvasContainer); + // 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 }; + var currentFrameRenderer = new pskl.rendering.FrameRenderer($(canvasContainer), renderingOptions, "tile-view"); + currentFrameRenderer.render(currentFrame); + + previewTileRoot.appendChild(canvasContainer); - if(tileNumber > 0 || this.framesheet.getFrameCount() > 1) { - // Add 'remove frame' button. - var deleteButton = document.createElement("button"); - deleteButton.setAttribute('rel', 'tooltip'); - deleteButton.setAttribute('data-placement', 'right'); - deleteButton.setAttribute('title', 'Delete this frame'); - deleteButton.className = "tile-overlay delete-frame-action"; - deleteButton.addEventListener('click', this.onDeleteButtonClick_.bind(this, tileNumber)); - previewTileRoot.appendChild(deleteButton); + if(tileNumber > 0 || this.framesheet.getFrameCount() > 1) { + // Add 'remove frame' button. + var deleteButton = document.createElement("button"); + deleteButton.setAttribute('rel', 'tooltip'); + deleteButton.setAttribute('data-placement', 'right'); + deleteButton.setAttribute('title', 'Delete this frame'); + deleteButton.className = "tile-overlay delete-frame-action"; + deleteButton.addEventListener('click', this.onDeleteButtonClick_.bind(this, tileNumber)); + previewTileRoot.appendChild(deleteButton); - // Add 'dragndrop handle'. - var dndHandle = document.createElement("div"); - dndHandle.className = "tile-overlay dnd-action"; - previewTileRoot.appendChild(dndHandle); - } - var tileCount = document.createElement("div"); - tileCount.className = "tile-overlay tile-count"; - tileCount.innerHTML = tileNumber; - previewTileRoot.appendChild(tileCount); - + // Add 'dragndrop handle'. + var dndHandle = document.createElement("div"); + dndHandle.className = "tile-overlay dnd-action"; + previewTileRoot.appendChild(dndHandle); + } + var tileCount = document.createElement("div"); + tileCount.className = "tile-overlay tile-count"; + tileCount.innerHTML = tileNumber; + previewTileRoot.appendChild(tileCount); + - return previewTileRoot; - }; + return previewTileRoot; + }; - ns.PreviewFilmController.prototype.onPreviewClick_ = function (index, evt) { - // has not class tile-action: - if(!evt.target.classList.contains('tile-overlay')) { - this.framesheet.setCurrentFrameIndex(index); - } - }; + ns.PreviewFilmController.prototype.onPreviewClick_ = function (index, evt) { + // has not class tile-action: + if(!evt.target.classList.contains('tile-overlay')) { + this.framesheet.setCurrentFrameIndex(index); + } + }; - ns.PreviewFilmController.prototype.onDeleteButtonClick_ = function (index, evt) { - this.framesheet.removeFrameByIndex(index); - $.publish(Events.LOCALSTORAGE_REQUEST); // Should come from model - this.updateScrollerOverflows(); - }; + ns.PreviewFilmController.prototype.onDeleteButtonClick_ = function (index, evt) { + this.framesheet.removeFrameByIndex(index); + $.publish(Events.LOCALSTORAGE_REQUEST); // Should come from model + this.updateScrollerOverflows(); + }; - ns.PreviewFilmController.prototype.onAddButtonClick_ = function (index, evt) { - this.framesheet.duplicateFrameByIndex(index); - $.publish(Events.LOCALSTORAGE_REQUEST); // Should come from model - this.framesheet.setCurrentFrameIndex(index + 1); - this.updateScrollerOverflows(); - }; + ns.PreviewFilmController.prototype.onAddButtonClick_ = function (index, evt) { + this.framesheet.duplicateFrameByIndex(index); + $.publish(Events.LOCALSTORAGE_REQUEST); // Should come from model + this.framesheet.setCurrentFrameIndex(index + 1); + this.updateScrollerOverflows(); + }; - /** - * Calculate the preview DPI depending on the framesheet size - */ - ns.PreviewFilmController.prototype.calculateDPI_ = function () { - var previewSize = 120, - framePixelHeight = this.framesheet.getCurrentFrame().getHeight(), - framePixelWidth = this.framesheet.getCurrentFrame().getWidth(); - // TODO (julz) : should have a utility to get a Size from framesheet easily (what about empty framesheets though ?) + /** + * Calculate the preview DPI depending on the framesheet size + */ + ns.PreviewFilmController.prototype.calculateDPI_ = function () { + var curFrame = this.framesheet.getCurrentFrame(), + frameHeight = curFrame.getHeight(), + frameWidth = curFrame.getWidth(), + maxFrameDim = Math.max(frameWidth, frameHeight); - return pskl.PixelUtils.calculateDPI(previewSize, previewSize, framePixelHeight, framePixelWidth); - }; + var previewHeight = Constants.PREVIEW_FILM_SIZE * frameHeight / maxFrameDim; + var previewWidth = Constants.PREVIEW_FILM_SIZE * frameWidth / maxFrameDim; + + return pskl.PixelUtils.calculateDPI(previewHeight, previewWidth, frameHeight, frameWidth) || 1; + }; })(); \ No newline at end of file diff --git a/js/controller/SettingsController.js b/js/controller/SettingsController.js index 7c54903d..3e7c237d 100644 --- a/js/controller/SettingsController.js +++ b/js/controller/SettingsController.js @@ -1,45 +1,45 @@ (function () { - var ns = $.namespace("pskl.controller"); + var ns = $.namespace("pskl.controller"); - ns.SettingsController = function () {}; + ns.SettingsController = function () {}; - /** - * @public - */ - ns.SettingsController.prototype.init = function() { + /** + * @public + */ + ns.SettingsController.prototype.init = function() { - // Highlight selected background picker: - var backgroundClass = pskl.UserSettings.get(pskl.UserSettings.CANVAS_BACKGROUND); - $('#background-picker-wrapper') - .find('.background-picker[data-background-class=' + backgroundClass + ']') - .addClass('selected'); + // Highlight selected background picker: + var backgroundClass = pskl.UserSettings.get(pskl.UserSettings.CANVAS_BACKGROUND); + $('#background-picker-wrapper') + .find('.background-picker[data-background-class=' + backgroundClass + ']') + .addClass('selected'); - // Initial state for grid display: - var show_grid = pskl.UserSettings.get(pskl.UserSettings.SHOW_GRID); - $('#show-grid').prop('checked', show_grid); + // Initial state for grid display: + var show_grid = pskl.UserSettings.get(pskl.UserSettings.SHOW_GRID); + $('#show-grid').prop('checked', show_grid); - // Expand drawer when clicking 'Settings' tab. - $('#settings').click(function(evt) { - $('.right-sticky-section').toggleClass('expanded'); - $('#settings').toggleClass('has-expanded-drawer'); - }); + // Expand drawer when clicking 'Settings' tab. + $('#settings').click(function(evt) { + $('.right-sticky-section').toggleClass('expanded'); + $('#settings').toggleClass('has-expanded-drawer'); + }); - // Handle grid display changes: - $('#show-grid').change($.proxy(function(evt) { - var checked = $('#show-grid').prop('checked'); - pskl.UserSettings.set(pskl.UserSettings.SHOW_GRID, checked); - }, this)); + // Handle grid display changes: + $('#show-grid').change($.proxy(function(evt) { + var checked = $('#show-grid').prop('checked'); + pskl.UserSettings.set(pskl.UserSettings.SHOW_GRID, checked); + }, this)); - // Handle canvas background changes: - $('#background-picker-wrapper').click(function(evt) { - var target = $(evt.target).closest('.background-picker'); - if (target.length) { - var backgroundClass = target.data('background-class'); - pskl.UserSettings.set(pskl.UserSettings.CANVAS_BACKGROUND, backgroundClass); + // Handle canvas background changes: + $('#background-picker-wrapper').click(function(evt) { + var target = $(evt.target).closest('.background-picker'); + if (target.length) { + var backgroundClass = target.data('background-class'); + pskl.UserSettings.set(pskl.UserSettings.CANVAS_BACKGROUND, backgroundClass); - $('.background-picker').removeClass('selected'); - target.addClass('selected'); - } - }); - }; + $('.background-picker').removeClass('selected'); + target.addClass('selected'); + } + }); + }; })(); \ No newline at end of file diff --git a/js/controller/ToolController.js b/js/controller/ToolController.js index aa48685e..4b38b2f9 100644 --- a/js/controller/ToolController.js +++ b/js/controller/ToolController.js @@ -1,99 +1,107 @@ (function () { - var ns = $.namespace("pskl.controller"); + var ns = $.namespace("pskl.controller"); - - ns.ToolController = function () { - - this.toolInstances = { - "simplePen" : new pskl.drawingtools.SimplePen(), - "verticalMirrorPen" : new pskl.drawingtools.VerticalMirrorPen(), - "eraser" : new pskl.drawingtools.Eraser(), - "paintBucket" : new pskl.drawingtools.PaintBucket(), - "stroke" : new pskl.drawingtools.Stroke(), - "rectangle" : new pskl.drawingtools.Rectangle(), - "circle" : new pskl.drawingtools.Circle(), - "move" : new pskl.drawingtools.Move(), - "rectangleSelect" : new pskl.drawingtools.RectangleSelect(), - "shapeSelect" : new pskl.drawingtools.ShapeSelect(), - "colorPicker" : new pskl.drawingtools.ColorPicker() - }; - - this.currentSelectedTool = this.toolInstances.simplePen; - this.previousSelectedTool = this.toolInstances.simplePen; + + ns.ToolController = function () { + + this.toolInstances = { + "simplePen" : new pskl.drawingtools.SimplePen(), + "verticalMirrorPen" : new pskl.drawingtools.VerticalMirrorPen(), + "eraser" : new pskl.drawingtools.Eraser(), + "paintBucket" : new pskl.drawingtools.PaintBucket(), + "stroke" : new pskl.drawingtools.Stroke(), + "rectangle" : new pskl.drawingtools.Rectangle(), + "circle" : new pskl.drawingtools.Circle(), + "move" : new pskl.drawingtools.Move(), + "rectangleSelect" : new pskl.drawingtools.RectangleSelect(), + "shapeSelect" : new pskl.drawingtools.ShapeSelect(), + "colorPicker" : new pskl.drawingtools.ColorPicker() }; - /** - * @private - */ - ns.ToolController.prototype.activateToolOnStage_ = function(tool) { - var stage = $("body"); - var previousSelectedToolClass = stage.data("selected-tool-class"); - if(previousSelectedToolClass) { - stage.removeClass(previousSelectedToolClass); - } - stage.addClass(tool.toolId); - stage.data("selected-tool-class", tool.toolId); - }; + this.currentSelectedTool = this.toolInstances.simplePen; + this.previousSelectedTool = this.toolInstances.simplePen; + }; - /** - * @private - */ - ns.ToolController.prototype.selectTool_ = function(tool) { - console.log("Selecting Tool:" , this.currentSelectedTool); - this.currentSelectedTool = tool; - this.activateToolOnStage_(this.currentSelectedTool); - $.publish(Events.TOOL_SELECTED, [tool]); - }; + /** + * @public + */ + ns.ToolController.prototype.init = function() { + this.createToolMarkup_(); - /** - * @private - */ - ns.ToolController.prototype.onToolIconClicked_ = function(evt) { - var target = $(evt.target); - var clickedTool = target.closest(".tool-icon"); + // Initialize tool: + // Set SimplePen as default selected tool: + this.selectTool_(this.toolInstances.simplePen); + // Activate listener on tool panel: + $("#tool-section").click($.proxy(this.onToolIconClicked_, this)); + }; - if(clickedTool.length) { - for(var tool in this.toolInstances) { - if (this.toolInstances[tool].toolId == clickedTool.data().toolId) { - this.selectTool_(this.toolInstances[tool]); + /** + * @private + */ + ns.ToolController.prototype.activateToolOnStage_ = function(tool) { + var stage = $("body"); + var previousSelectedToolClass = stage.data("selected-tool-class"); + if(previousSelectedToolClass) { + stage.removeClass(previousSelectedToolClass); + } + stage.addClass(tool.toolId); + stage.data("selected-tool-class", tool.toolId); + }; - // Show tool as selected: - $('#tool-section .tool-icon.selected').removeClass('selected'); - clickedTool.addClass('selected'); - } - } - } - }; + /** + * @private + */ + ns.ToolController.prototype.selectTool_ = function(tool) { + console.log("Selecting Tool:" , this.currentSelectedTool); + this.currentSelectedTool = tool; + this.activateToolOnStage_(this.currentSelectedTool); + $.publish(Events.TOOL_SELECTED, [tool]); + }; - /** - * @private - */ - ns.ToolController.prototype.createToolMarkup_ = function() { - var currentTool, toolMarkup = '', extraClass; - // TODO(vincz): Tools rendering order is not enforced by the data stucture (this.toolInstances), fix that. - for (var toolKey in this.toolInstances) { - currentTool = this.toolInstances[toolKey]; - extraClass = currentTool.toolId; - if (this.currentSelectedTool == currentTool) { - extraClass = extraClass + " selected"; - } - toolMarkup += '
  • '; - } - $('#tools-container').html(toolMarkup); - }; + /** + * @private + */ + ns.ToolController.prototype.onToolIconClicked_ = function(evt) { + var target = $(evt.target); + var clickedTool = target.closest(".tool-icon"); - /** - * @public - */ - ns.ToolController.prototype.init = function() { + if(clickedTool.length) { + var toolId = clickedTool.data().toolId; + var tool = this.getToolById_(toolId); + if (tool) { + this.selectTool_(tool); - this.createToolMarkup_(); + // Show tool as selected: + $('#tool-section .tool-icon.selected').removeClass('selected'); + clickedTool.addClass('selected'); + } + } + }; - // Initialize tool: - // Set SimplePen as default selected tool: - this.selectTool_(this.toolInstances.simplePen); - // Activate listener on tool panel: - $("#tool-section").click($.proxy(this.onToolIconClicked_, this)); - }; + ns.ToolController.prototype.getToolById_ = function (toolId) { + for(var key in this.toolInstances) { + if (this.toolInstances[key].toolId == toolId) { + return this.toolInstances[key]; + } + } + return null; + }; + + /** + * @private + */ + ns.ToolController.prototype.createToolMarkup_ = function() { + var currentTool, toolMarkup = '', extraClass; + // TODO(vincz): Tools rendering order is not enforced by the data stucture (this.toolInstances), fix that. + for (var toolKey in this.toolInstances) { + currentTool = this.toolInstances[toolKey]; + extraClass = currentTool.toolId; + if (this.currentSelectedTool == currentTool) { + extraClass = extraClass + " selected"; + } + toolMarkup += '
  • '; + } + $('#tools-container').html(toolMarkup); + }; })(); \ No newline at end of file diff --git a/js/drawingtools/BaseTool.js b/js/drawingtools/BaseTool.js index 32524b68..f1d77ad0 100644 --- a/js/drawingtools/BaseTool.js +++ b/js/drawingtools/BaseTool.js @@ -4,69 +4,69 @@ * @require pskl.utils */ (function() { - var ns = $.namespace("pskl.drawingtools"); + var ns = $.namespace("pskl.drawingtools"); - ns.BaseTool = function() {}; + ns.BaseTool = function() {}; - ns.BaseTool.prototype.applyToolAt = function(col, row, color, frame, overlay) {}; - - ns.BaseTool.prototype.moveToolAt = function(col, row, color, frame, overlay) {}; + ns.BaseTool.prototype.applyToolAt = function(col, row, color, frame, overlay) {}; + + ns.BaseTool.prototype.moveToolAt = function(col, row, color, frame, overlay) {}; - ns.BaseTool.prototype.moveUnactiveToolAt = function(col, row, color, frame, overlay) { - if (overlay.containsPixel(col, row)) { - if (!isNaN(this.highlightedPixelCol) && - !isNaN(this.highlightedPixelRow) && - (this.highlightedPixelRow != row || - this.highlightedPixelCol != col)) { + ns.BaseTool.prototype.moveUnactiveToolAt = function(col, row, color, frame, overlay) { + if (overlay.containsPixel(col, row)) { + if (!isNaN(this.highlightedPixelCol) && + !isNaN(this.highlightedPixelRow) && + (this.highlightedPixelRow != row || + this.highlightedPixelCol != col)) { - // Clean the previously highlighted pixel: - overlay.clear(); - } + // Clean the previously highlighted pixel: + overlay.clear(); + } - // Show the current pixel targeted by the tool: - overlay.setPixel(col, row, Constants.TOOL_TARGET_HIGHLIGHT_COLOR); + // Show the current pixel targeted by the tool: + overlay.setPixel(col, row, Constants.TOOL_TARGET_HIGHLIGHT_COLOR); - this.highlightedPixelCol = col; - this.highlightedPixelRow = row; - } - }; + this.highlightedPixelCol = col; + this.highlightedPixelRow = row; + } + }; - ns.BaseTool.prototype.releaseToolAt = function(col, row, color, frame, overlay) {}; + ns.BaseTool.prototype.releaseToolAt = function(col, row, color, frame, overlay) {}; - /** - * 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; + /** + * 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){ + while(true){ - // Do what you need to for this - pixels.push({"col": x0, "row": y0}); + // 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; - }; + 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/Circle.js b/js/drawingtools/Circle.js index e9a7c5e6..a665dc96 100644 --- a/js/drawingtools/Circle.js +++ b/js/drawingtools/Circle.js @@ -4,82 +4,82 @@ * @require pskl.utils */ (function() { - var ns = $.namespace("pskl.drawingtools"); + var ns = $.namespace("pskl.drawingtools"); - ns.Circle = function() { - this.toolId = "tool-circle"; - this.helpText = "Circle tool"; - - // Circle's first point coordinates (set in applyToolAt) - this.startCol = null; - this.startRow = null; - }; + ns.Circle = function() { + this.toolId = "tool-circle"; + this.helpText = "Circle tool"; + + // Circle's first point coordinates (set in applyToolAt) + this.startCol = null; + this.startRow = null; + }; - pskl.utils.inherit(ns.Circle, ns.BaseTool); - - /** - * @override - */ - ns.Circle.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: - overlay.setPixel(col, row, color); - }; + pskl.utils.inherit(ns.Circle, ns.BaseTool); + + /** + * @override + */ + ns.Circle.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: + overlay.setPixel(col, row, color); + }; - ns.Circle.prototype.moveToolAt = function(col, row, color, frame, overlay) { - overlay.clear(); - if(color == Constants.TRANSPARENT_COLOR) { - color = Constants.SELECTION_TRANSPARENT_COLOR; - } + ns.Circle.prototype.moveToolAt = function(col, row, color, frame, overlay) { + overlay.clear(); + if(color == Constants.TRANSPARENT_COLOR) { + color = Constants.SELECTION_TRANSPARENT_COLOR; + } - // draw in overlay - this.drawCircle_(col, row, color, overlay); - }; + // draw in overlay + this.drawCircle_(col, row, color, overlay); + }; - /** - * @override - */ - ns.Circle.prototype.releaseToolAt = function(col, row, color, frame, overlay) { - overlay.clear(); - if(frame.containsPixel(col, row)) { // cancel if outside of canvas - // draw in frame to finalize - this.drawCircle_(col, row, color, frame); - } - }; + /** + * @override + */ + ns.Circle.prototype.releaseToolAt = function(col, row, color, frame, overlay) { + overlay.clear(); + if(frame.containsPixel(col, row)) { // cancel if outside of canvas + // draw in frame to finalize + this.drawCircle_(col, row, color, frame); + } + }; - ns.Circle.prototype.drawCircle_ = function (col, row, color, targetFrame) { - var circlePoints = this.getCirclePixels_(this.startCol, this.startRow, col, row); - for(var i = 0; i< circlePoints.length; i++) { - // Change model: - targetFrame.setPixel(circlePoints[i].col, circlePoints[i].row, color); - } - }; + ns.Circle.prototype.drawCircle_ = function (col, row, color, targetFrame) { + var circlePoints = this.getCirclePixels_(this.startCol, this.startRow, col, row); + for(var i = 0; i< circlePoints.length; i++) { + // Change model: + targetFrame.setPixel(circlePoints[i].col, circlePoints[i].row, color); + } + }; - ns.Circle.prototype.getCirclePixels_ = function (x0, y0, x1, y1) { - var coords = pskl.PixelUtils.getOrderedRectangleCoordinates(x0, y0, x1, y1); - var xC = (coords.x0 + coords.x1)/2; - var yC = (coords.y0 + coords.y1)/2; - - var rX = coords.x1 - xC; - var rY = coords.y1 - yC; + ns.Circle.prototype.getCirclePixels_ = function (x0, y0, x1, y1) { + var coords = pskl.PixelUtils.getOrderedRectangleCoordinates(x0, y0, x1, y1); + var xC = (coords.x0 + coords.x1)/2; + var yC = (coords.y0 + coords.y1)/2; + + var rX = coords.x1 - xC; + var rY = coords.y1 - yC; - var pixels = []; - var x, y, angle; - for (x = coords.x0 ; x < coords.x1 ; x++) { - angle = Math.acos((x - xC)/rX); - y = Math.round(rY * Math.sin(angle) + yC); - pixels.push({"col": x, "row": y}); - pixels.push({"col": 2*xC - x, "row": 2*yC - y}); - } + var pixels = []; + var x, y, angle; + for (x = coords.x0 ; x < coords.x1 ; x++) { + angle = Math.acos((x - xC)/rX); + y = Math.round(rY * Math.sin(angle) + yC); + pixels.push({"col": x, "row": y}); + pixels.push({"col": 2*xC - x, "row": 2*yC - y}); + } - for (y = coords.y0 ; y < coords.y1 ; y++) { - angle = Math.asin((y - yC)/rY); - x = Math.round(rX * Math.cos(angle) + xC); - pixels.push({"col": x, "row": y}); - pixels.push({"col": 2*xC - x, "row": 2*yC - y}); - } - return pixels; - }; + for (y = coords.y0 ; y < coords.y1 ; y++) { + angle = Math.asin((y - yC)/rY); + x = Math.round(rX * Math.cos(angle) + xC); + pixels.push({"col": x, "row": y}); + pixels.push({"col": 2*xC - x, "row": 2*yC - y}); + } + return pixels; + }; })(); diff --git a/js/drawingtools/ColorPicker.js b/js/drawingtools/ColorPicker.js index f204afc4..9c5d72c6 100644 --- a/js/drawingtools/ColorPicker.js +++ b/js/drawingtools/ColorPicker.js @@ -4,26 +4,26 @@ * @require pskl.utils */ (function() { - var ns = $.namespace("pskl.drawingtools"); + var ns = $.namespace("pskl.drawingtools"); - ns.ColorPicker = function() { - this.toolId = "tool-colorpicker"; - this.helpText = "Color picker"; - }; + ns.ColorPicker = function() { + this.toolId = "tool-colorpicker"; + this.helpText = "Color picker"; + }; - pskl.utils.inherit(ns.ColorPicker, ns.BaseTool); - - /** - * @override - */ - ns.ColorPicker.prototype.applyToolAt = function(col, row, color, frame, overlay, context) { - if (frame.containsPixel(col, row)) { - var sampledColor = frame.getPixel(col, row); - if (context.button == Constants.LEFT_BUTTON) { - $.publish(Events.PRIMARY_COLOR_SELECTED, [sampledColor]); - } else if (context.button == Constants.RIGHT_BUTTON) { - $.publish(Events.SECONDARY_COLOR_SELECTED, [sampledColor]); - } - } - }; + pskl.utils.inherit(ns.ColorPicker, ns.BaseTool); + + /** + * @override + */ + ns.ColorPicker.prototype.applyToolAt = function(col, row, color, frame, overlay, context) { + if (frame.containsPixel(col, row)) { + var sampledColor = frame.getPixel(col, row); + if (context.button == Constants.LEFT_BUTTON) { + $.publish(Events.PRIMARY_COLOR_SELECTED, [sampledColor]); + } else if (context.button == Constants.RIGHT_BUTTON) { + $.publish(Events.SECONDARY_COLOR_SELECTED, [sampledColor]); + } + } + }; })(); diff --git a/js/drawingtools/Eraser.js b/js/drawingtools/Eraser.js index 2db273c4..fd3cb7e3 100644 --- a/js/drawingtools/Eraser.js +++ b/js/drawingtools/Eraser.js @@ -4,20 +4,20 @@ * @require Constants * @require pskl.utils */ - (function() { - var ns = $.namespace("pskl.drawingtools"); +(function() { + var ns = $.namespace("pskl.drawingtools"); - ns.Eraser = function() { - this.toolId = "tool-eraser"; - this.helpText = "Eraser tool"; - }; + ns.Eraser = function() { + this.toolId = "tool-eraser"; + this.helpText = "Eraser tool"; + }; - pskl.utils.inherit(ns.Eraser, ns.SimplePen); + pskl.utils.inherit(ns.Eraser, ns.SimplePen); - /** - * @override - */ - ns.Eraser.prototype.applyToolAt = function(col, row, color, frame, overlay) { - this.superclass.applyToolAt.call(this, col, row, Constants.TRANSPARENT_COLOR, frame, overlay); - }; + /** + * @override + */ + 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 914123ce..e561b91a 100644 --- a/js/drawingtools/Move.js +++ b/js/drawingtools/Move.js @@ -4,51 +4,51 @@ * @require pskl.utils */ (function() { - var ns = $.namespace("pskl.drawingtools"); + var ns = $.namespace("pskl.drawingtools"); - ns.Move = function() { - this.toolId = "tool-move"; - this.helpText = "Move tool"; - - // Stroke's first point coordinates (set in applyToolAt) - this.startCol = null; - this.startRow = null; - }; + ns.Move = function() { + this.toolId = "tool-move"; + this.helpText = "Move tool"; + + // Stroke's first point coordinates (set in applyToolAt) + this.startCol = null; + this.startRow = null; + }; - pskl.utils.inherit(ns.Move, ns.BaseTool); - - /** - * @override - */ - ns.Move.prototype.applyToolAt = function(col, row, color, frame, overlay) { - this.startCol = col; - this.startRow = row; - this.frameClone = frame.clone(); - }; + pskl.utils.inherit(ns.Move, ns.BaseTool); + + /** + * @override + */ + ns.Move.prototype.applyToolAt = function(col, row, color, frame, overlay) { + this.startCol = col; + this.startRow = row; + this.frameClone = frame.clone(); + }; - ns.Move.prototype.moveToolAt = function(col, row, color, frame, overlay) { - var colDiff = col - this.startCol, rowDiff = row - this.startRow; - this.shiftFrame(colDiff, rowDiff, frame, this.frameClone); - }; + ns.Move.prototype.moveToolAt = function(col, row, color, frame, overlay) { + var colDiff = col - this.startCol, rowDiff = row - this.startRow; + this.shiftFrame(colDiff, rowDiff, frame, this.frameClone); + }; - ns.Move.prototype.shiftFrame = function (colDiff, rowDiff, frame, reference) { - var color; - for (var col = 0 ; col < frame.getWidth() ; col++) { - for (var row = 0 ; row < frame.getHeight() ; row++) { - if (reference.containsPixel(col - colDiff, row - rowDiff)) { - color = reference.getPixel(col - colDiff, row - rowDiff); - } else { - color = Constants.TRANSPARENT_COLOR; - } - frame.setPixel(col, row, color); - } - } - }; + ns.Move.prototype.shiftFrame = function (colDiff, rowDiff, frame, reference) { + var color; + for (var col = 0 ; col < frame.getWidth() ; col++) { + for (var row = 0 ; row < frame.getHeight() ; row++) { + if (reference.containsPixel(col - colDiff, row - rowDiff)) { + color = reference.getPixel(col - colDiff, row - rowDiff); + } else { + color = Constants.TRANSPARENT_COLOR; + } + frame.setPixel(col, row, color); + } + } + }; - /** - * @override - */ - ns.Move.prototype.releaseToolAt = function(col, row, color, frame, overlay) { - this.moveToolAt(col, row, color, frame, overlay); - }; + /** + * @override + */ + 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 b42d94a4..428596e0 100644 --- a/js/drawingtools/PaintBucket.js +++ b/js/drawingtools/PaintBucket.js @@ -4,22 +4,22 @@ * @require pskl.utils */ (function() { - var ns = $.namespace("pskl.drawingtools"); + var ns = $.namespace("pskl.drawingtools"); - ns.PaintBucket = function() { - this.toolId = "tool-paint-bucket"; - this.helpText = "Paint bucket tool"; - }; + ns.PaintBucket = function() { + this.toolId = "tool-paint-bucket"; + this.helpText = "Paint bucket tool"; + }; - pskl.utils.inherit(ns.PaintBucket, ns.BaseTool); + pskl.utils.inherit(ns.PaintBucket, ns.BaseTool); - /** - * @override - */ - ns.PaintBucket.prototype.applyToolAt = function(col, row, color, frame, overlay) { + /** + * @override + */ + ns.PaintBucket.prototype.applyToolAt = function(col, row, color, frame, overlay) { - pskl.PixelUtils.paintSimilarConnectedPixelsFromFrame(frame, col, row, color); - }; + pskl.PixelUtils.paintSimilarConnectedPixelsFromFrame(frame, col, row, color); + }; })(); diff --git a/js/drawingtools/Rectangle.js b/js/drawingtools/Rectangle.js index 686a7786..532afd35 100644 --- a/js/drawingtools/Rectangle.js +++ b/js/drawingtools/Rectangle.js @@ -4,56 +4,56 @@ * @require pskl.utils */ (function() { - var ns = $.namespace("pskl.drawingtools"); + var ns = $.namespace("pskl.drawingtools"); - ns.Rectangle = function() { - this.toolId = "tool-rectangle"; - this.helpText = "Rectangle tool"; - - // Rectangle's first point coordinates (set in applyToolAt) - this.startCol = null; - this.startRow = null; - }; + ns.Rectangle = function() { + this.toolId = "tool-rectangle"; + this.helpText = "Rectangle tool"; + + // Rectangle's first point coordinates (set in applyToolAt) + this.startCol = null; + this.startRow = null; + }; - pskl.utils.inherit(ns.Rectangle, ns.BaseTool); - - /** - * @override - */ - 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: - overlay.setPixel(col, row, color); - }; + pskl.utils.inherit(ns.Rectangle, ns.BaseTool); + + /** + * @override + */ + 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: + overlay.setPixel(col, row, color); + }; - ns.Rectangle.prototype.moveToolAt = function(col, row, color, frame, overlay) { - overlay.clear(); - if(color == Constants.TRANSPARENT_COLOR) { - color = Constants.SELECTION_TRANSPARENT_COLOR; - } + ns.Rectangle.prototype.moveToolAt = function(col, row, color, frame, overlay) { + overlay.clear(); + if(color == Constants.TRANSPARENT_COLOR) { + color = Constants.SELECTION_TRANSPARENT_COLOR; + } - // draw in overlay - this.drawRectangle_(col, row, color, overlay); - }; + // draw in overlay + this.drawRectangle_(col, row, color, overlay); + }; - /** - * @override - */ - ns.Rectangle.prototype.releaseToolAt = function(col, row, color, frame, overlay) { - overlay.clear(); - if(frame.containsPixel(col, row)) { // cancel if outside of canvas - // draw in frame to finalize - this.drawRectangle_(col, row, color, frame); - } - }; + /** + * @override + */ + ns.Rectangle.prototype.releaseToolAt = function(col, row, color, frame, overlay) { + overlay.clear(); + if(frame.containsPixel(col, row)) { // cancel if outside of canvas + // draw in frame to finalize + this.drawRectangle_(col, row, color, frame); + } + }; - ns.Rectangle.prototype.drawRectangle_ = function (col, row, color, targetFrame) { - var strokePoints = pskl.PixelUtils.getBoundRectanglePixels(this.startCol, this.startRow, col, row); - for(var i = 0; i< strokePoints.length; i++) { - // Change model: - targetFrame.setPixel(strokePoints[i].col, strokePoints[i].row, color); - } - }; + ns.Rectangle.prototype.drawRectangle_ = function (col, row, color, targetFrame) { + var strokePoints = pskl.PixelUtils.getBoundRectanglePixels(this.startCol, this.startRow, col, row); + for(var i = 0; i< strokePoints.length; i++) { + // Change model: + targetFrame.setPixel(strokePoints[i].col, strokePoints[i].row, color); + } + }; })(); diff --git a/js/drawingtools/SimplePen.js b/js/drawingtools/SimplePen.js index dcbd89fa..1f7d482a 100644 --- a/js/drawingtools/SimplePen.js +++ b/js/drawingtools/SimplePen.js @@ -4,46 +4,46 @@ * @require pskl.utils */ (function() { - var ns = $.namespace("pskl.drawingtools"); + var ns = $.namespace("pskl.drawingtools"); - ns.SimplePen = function() { - this.toolId = "tool-pen"; - this.helpText = "Pen tool"; + ns.SimplePen = function() { + this.toolId = "tool-pen"; + this.helpText = "Pen tool"; - this.previousCol = null; - this.previousRow = null; + this.previousCol = null; + this.previousRow = null; - }; + }; - pskl.utils.inherit(ns.SimplePen, ns.BaseTool); - - /** - * @override - */ - ns.SimplePen.prototype.applyToolAt = function(col, row, color, frame, overlay) { - if (frame.containsPixel(col, row)) { - frame.setPixel(col, row, color); - } - this.previousCol = col; - this.previousRow = row; - }; + pskl.utils.inherit(ns.SimplePen, ns.BaseTool); + + /** + * @override + */ + ns.SimplePen.prototype.applyToolAt = function(col, row, color, frame, overlay) { + if (frame.containsPixel(col, row)) { + frame.setPixel(col, row, color); + } + this.previousCol = col; + this.previousRow = row; + }; - 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 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= 0 && row >= 0 && col < this.pixels.length && row < this.pixels[0].length; - }; + ns.Frame.prototype.containsPixel = function (col, row) { + return col >= 0 && row >= 0 && col < this.pixels.length && row < this.pixels[0].length; + }; - ns.Frame.prototype.saveState = function () { - // remove all states past current state - this.previousStates.length = this.stateIndex + 1; - // push new state - this.previousStates.push(this.getPixels()); - // set the stateIndex to latest saved state - this.stateIndex = this.previousStates.length - 1; - }; + ns.Frame.prototype.saveState = function () { + // remove all states past current state + this.previousStates.length = this.stateIndex + 1; + // push new state + this.previousStates.push(this.getPixels()); + // set the stateIndex to latest saved state + this.stateIndex = this.previousStates.length - 1; + }; - ns.Frame.prototype.loadPreviousState = function () { - if (this.stateIndex > 0) { - this.stateIndex--; - this.setPixels(this.previousStates[this.stateIndex]); - } - }; + ns.Frame.prototype.loadPreviousState = function () { + if (this.stateIndex > 0) { + this.stateIndex--; + this.setPixels(this.previousStates[this.stateIndex]); + } + }; - ns.Frame.prototype.loadNextState = function () { - if (this.stateIndex < this.previousStates.length - 1) { - this.stateIndex++; - this.setPixels(this.previousStates[this.stateIndex]); - } - }; + ns.Frame.prototype.loadNextState = function () { + if (this.stateIndex < this.previousStates.length - 1) { + this.stateIndex++; + this.setPixels(this.previousStates[this.stateIndex]); + } + }; - ns.Frame.prototype.isSameSize = function (otherFrame) { - return this.getHeight() == otherFrame.getHeight() && this.getWidth() == otherFrame.getWidth(); - }; + ns.Frame.prototype.isSameSize = function (otherFrame) { + return this.getHeight() == otherFrame.getHeight() && this.getWidth() == otherFrame.getWidth(); + }; })(); \ No newline at end of file diff --git a/js/model/FrameSheet.js b/js/model/FrameSheet.js index 8bc28ba5..feb62056 100644 --- a/js/model/FrameSheet.js +++ b/js/model/FrameSheet.js @@ -1,157 +1,157 @@ (function () { - var ns = $.namespace("pskl.model"); - ns.FrameSheet = function (height, width) { - this.width = width; - this.height = height; - this.frames = []; - this.currentFrameIndex = 0; - }; + var ns = $.namespace("pskl.model"); + ns.FrameSheet = function (height, width) { + this.width = width; + this.height = height; + this.frames = []; + this.currentFrameIndex = 0; + }; - ns.FrameSheet.prototype.getHeight = function () { - return this.height; - }; + ns.FrameSheet.prototype.getHeight = function () { + return this.height; + }; - ns.FrameSheet.prototype.getWidth = function () { - return this.width; - }; + ns.FrameSheet.prototype.getWidth = function () { + return this.width; + }; - ns.FrameSheet.prototype.addEmptyFrame = function () { - this.addFrame(ns.Frame.createEmpty(this.width, this.height)); - }; + ns.FrameSheet.prototype.addEmptyFrame = function () { + this.addFrame(ns.Frame.createEmpty(this.width, this.height)); + }; - ns.FrameSheet.prototype.addFrame = function (frame) { - this.frames.push(frame); - }; + ns.FrameSheet.prototype.addFrame = function (frame) { + this.frames.push(frame); + }; - ns.FrameSheet.prototype.getFrameCount = function () { - return this.frames.length; - }; + ns.FrameSheet.prototype.getFrameCount = function () { + return this.frames.length; + }; - ns.FrameSheet.prototype.getCurrentFrame = function () { - return this.frames[this.currentFrameIndex]; - }; + ns.FrameSheet.prototype.getCurrentFrame = function () { + return this.frames[this.currentFrameIndex]; + }; - ns.FrameSheet.prototype.setCurrentFrameIndex = function (index) { - this.currentFrameIndex = index; - $.publish(Events.CURRENT_FRAME_SET, [this.getCurrentFrame()]); - $.publish(Events.FRAMESHEET_RESET); // Is it no to overkill to have this here ? - }; + ns.FrameSheet.prototype.setCurrentFrameIndex = function (index) { + this.currentFrameIndex = index; + $.publish(Events.CURRENT_FRAME_SET, [this.getCurrentFrame()]); + $.publish(Events.FRAMESHEET_RESET); // Is it no to overkill to have this here ? + }; - ns.FrameSheet.prototype.getUsedColors = function() { - var colors = {}; - for (var frameIndex=0; frameIndex < this.frames.length; frameIndex++) { - var frame = this.frames[frameIndex]; - for (var i = 0, width = frame.getWidth(); i < width ; i++) { - var line = frame[i]; - for (var j = 0, height = frame.getHeight() ; j < height ; j++) { - var pixel = frame.getPixel(i, j); - colors[pixel] = pixel; - } - } - } - return colors; - }; + ns.FrameSheet.prototype.getUsedColors = function() { + var colors = {}; + for (var frameIndex=0; frameIndex < this.frames.length; frameIndex++) { + var frame = this.frames[frameIndex]; + for (var i = 0, width = frame.getWidth(); i < width ; i++) { + var line = frame[i]; + for (var j = 0, height = frame.getHeight() ; j < height ; j++) { + var pixel = frame.getPixel(i, j); + colors[pixel] = pixel; + } + } + } + return colors; + }; - // Could be used to pass around model using long GET param (good enough for simple models) and - // do some temporary locastorage - ns.FrameSheet.prototype.serialize = function() { - var serializedFrames = []; - for (var i = 0 ; i < this.frames.length ; i++) { - serializedFrames.push(this.frames[i].serialize()); - } - return '[' + serializedFrames.join(",") + ']'; - //return JSON.stringify(frames); - }; + // Could be used to pass around model using long GET param (good enough for simple models) and + // do some temporary locastorage + ns.FrameSheet.prototype.serialize = function() { + var serializedFrames = []; + for (var i = 0 ; i < this.frames.length ; i++) { + serializedFrames.push(this.frames[i].serialize()); + } + return '[' + serializedFrames.join(",") + ']'; + //return JSON.stringify(frames); + }; - /** - * Load a framesheet from a model that might have been persisted in db / localstorage - * Overrides existing frames. - * @param {String} serialized - */ - ns.FrameSheet.prototype.deserialize = function (serialized) { - try { - this.load(JSON.parse(serialized)); - } catch (e) { - throw "Could not load serialized framesheet : " + e.message; - } - }; + /** + * Load a framesheet from a model that might have been persisted in db / localstorage + * Overrides existing frames. + * @param {String} serialized + */ + ns.FrameSheet.prototype.deserialize = function (serialized) { + try { + this.load(JSON.parse(serialized)); + } catch (e) { + throw "Could not load serialized framesheet : " + e.message; + } + }; - /** - * Load a framesheet from a model that might have been persisted in db / localstorage - * Overrides existing frames. - * @param {String} serialized - */ - ns.FrameSheet.prototype.load = function (framesheet) { - this.frames = []; - for (var i = 0 ; i < framesheet.length ; i++) { - var frameCfg = framesheet[i]; - this.addFrame(new ns.Frame(frameCfg)); - } + /** + * Load a framesheet from a model that might have been persisted in db / localstorage + * Overrides existing frames. + * @param {String} serialized + */ + ns.FrameSheet.prototype.load = function (framesheet) { + this.frames = []; + for (var i = 0 ; i < framesheet.length ; i++) { + var frameCfg = framesheet[i]; + this.addFrame(new ns.Frame(frameCfg)); + } - if (this.hasFrameAtIndex(0)) { - this.height = this.getFrameByIndex(0).getHeight(); - this.width = this.getFrameByIndex(0).getWidth(); - this.setCurrentFrameIndex(0); - $.publish(Events.FRAME_SIZE_CHANGED); - } + if (this.hasFrameAtIndex(0)) { + this.height = this.getFrameByIndex(0).getHeight(); + this.width = this.getFrameByIndex(0).getWidth(); + this.setCurrentFrameIndex(0); + $.publish(Events.FRAME_SIZE_CHANGED); + } - $.publish(Events.FRAMESHEET_RESET); - }; + $.publish(Events.FRAMESHEET_RESET); + }; - - ns.FrameSheet.prototype.hasFrameAtIndex = function(index) { - return (index >= 0 && index < this.getFrameCount()); - }; + + ns.FrameSheet.prototype.hasFrameAtIndex = function(index) { + return (index >= 0 && index < this.getFrameCount()); + }; - ns.FrameSheet.prototype.getFrameByIndex = function(index) { - if (isNaN(index)) { - throw "Bad argument value for getFrameByIndex method: <" + index + ">"; - } + ns.FrameSheet.prototype.getFrameByIndex = function(index) { + if (isNaN(index)) { + throw "Bad argument value for getFrameByIndex method: <" + index + ">"; + } - if (!this.hasFrameAtIndex(index)) { - throw "Out of bound index for frameSheet object."; - } + if (!this.hasFrameAtIndex(index)) { + throw "Out of bound index for frameSheet object."; + } - return this.frames[index]; - }; + return this.frames[index]; + }; - ns.FrameSheet.prototype.removeFrameByIndex = function(index) { - if(!this.hasFrameAtIndex(index)) { - throw "Out of bound index for frameSheet object."; - } - this.frames.splice(index, 1); + ns.FrameSheet.prototype.removeFrameByIndex = function(index) { + if(!this.hasFrameAtIndex(index)) { + throw "Out of bound index for frameSheet object."; + } + this.frames.splice(index, 1); - // Current frame index might not be valid anymore - if (!this.hasFrameAtIndex(this.currentFrameIndex)) { - // if not select last frame available - this.setCurrentFrameIndex(this.getFrameCount() - 1); - } + // Current frame index might not be valid anymore + if (!this.hasFrameAtIndex(this.currentFrameIndex)) { + // if not select last frame available + this.setCurrentFrameIndex(this.getFrameCount() - 1); + } - $.publish(Events.FRAMESHEET_RESET); - }; + $.publish(Events.FRAMESHEET_RESET); + }; - ns.FrameSheet.prototype.duplicateFrameByIndex = function(index) { - var frame = this.getFrameByIndex(index); - this.frames.splice(index + 1, 0, frame.clone()); - }; + ns.FrameSheet.prototype.duplicateFrameByIndex = function(index) { + var frame = this.getFrameByIndex(index); + this.frames.splice(index + 1, 0, frame.clone()); + }; - ns.FrameSheet.prototype.moveFrame = function(originIndex, destinationIndex) { - this.frames.splice(destinationIndex, 0, this.frames.splice(originIndex, 1)[0]); - }; + ns.FrameSheet.prototype.moveFrame = function(originIndex, destinationIndex) { + this.frames.splice(destinationIndex, 0, this.frames.splice(originIndex, 1)[0]); + }; - ns.FrameSheet.prototype.swapFrames = function(indexFrame1, indexFrame2) { - if(isNaN(indexFrame1) || isNaN(indexFrame1) || - (!this.hasFrameAtIndex(indexFrame1) && !this.hasFrameAtIndex(indexFrame2))) { - throw "Bad indexes for swapFrames Framesheet function."; - } - if(indexFrame1 == indexFrame2) { - return; - } - else { - var swapFrame = this.frames[indexFrame1]; - this.frames[indexFrame1] = this.frames[indexFrame2]; - this.frames[indexFrame2] = swapFrame; - } - }; + ns.FrameSheet.prototype.swapFrames = function(indexFrame1, indexFrame2) { + if(isNaN(indexFrame1) || isNaN(indexFrame1) || + (!this.hasFrameAtIndex(indexFrame1) && !this.hasFrameAtIndex(indexFrame2))) { + throw "Bad indexes for swapFrames Framesheet function."; + } + if(indexFrame1 == indexFrame2) { + return; + } + else { + var swapFrame = this.frames[indexFrame1]; + this.frames[indexFrame1] = this.frames[indexFrame2]; + this.frames[indexFrame2] = swapFrame; + } + }; })(); \ No newline at end of file diff --git a/js/piskel.js b/js/piskel.js index 9f68797d..72ad392a 100644 --- a/js/piskel.js +++ b/js/piskel.js @@ -17,6 +17,7 @@ var frameSize = this.readSizeFromURL_(); frameSheet = new pskl.model.FrameSheet(frameSize.height, frameSize.width); frameSheet.addEmptyFrame(); + frameSheet.setCurrentFrameIndex(0); /** * True when piskel is running in static mode (no back end needed). @@ -25,26 +26,20 @@ this.isStaticVersion = !pskl.appEngineToken_; this.drawingController = new pskl.controller.DrawingController(frameSheet, $('#drawing-canvas-container')); + this.drawingController.init(); + this.animationController = new pskl.controller.AnimatedPreviewController(frameSheet, $('#preview-canvas-container')); - this.previewsController = new pskl.controller.PreviewFilmController(frameSheet, $('#preview-list')); - this.settingsController = new pskl.controller.SettingsController(); - - // To catch the current active frame, the selection manager have to be initialized before - // the 'frameSheet.setCurrentFrameIndex(0);' line below. - // TODO(vincz): Slice each constructor to have: - // - an event(s) listening init - // - an event(s) triggering init - // All listeners will be hook in a first step, then all event triggering inits will be called - // in a second batch. - this.selectionManager = new pskl.selection.SelectionManager(frameSheet, this.drawingController.overlayFrame); - - // DO NOT MOVE THIS LINE (see comment above) - frameSheet.setCurrentFrameIndex(0); - this.animationController.init(); + + this.previewsController = new pskl.controller.PreviewFilmController(frameSheet, $('#preview-list')); this.previewsController.init(); + + this.settingsController = new pskl.controller.SettingsController(); this.settingsController.init(); + this.selectionManager = new pskl.selection.SelectionManager(frameSheet); + this.selectionManager.init(); + this.historyService = new pskl.service.HistoryService(frameSheet); this.historyService.init(); @@ -57,6 +52,28 @@ this.localStorageService = new pskl.service.LocalStorageService(frameSheet); this.localStorageService.init(); + this.toolController = new pskl.controller.ToolController(); + this.toolController.init(); + + this.paletteController = new pskl.controller.PaletteController(); + this.paletteController.init(frameSheet); + + var drawingLoop = new pskl.rendering.DrawingLoop(); + drawingLoop.addCallback(this.render, this); + drawingLoop.start(); + + // Init (event-delegated) bootstrap tooltips: + $('body').tooltip({ + selector: '[rel=tooltip]' + }); + + + /** + * True when piskel is running in static mode (no back end needed). + * When started from APP Engine, appEngineToken_ (Boolean) should be set on window.pskl + */ + this.isStaticVersion = !pskl.appEngineToken_; + if (this.isStaticVersion) { var framesheetId = this.readFramesheetIdFromURL_(); if (framesheetId) { @@ -65,7 +82,6 @@ }]); this.loadFramesheetFromService(framesheetId); } else { - this.finishInit(); this.localStorageService.displayRestoreNotification(); } } else { @@ -73,17 +89,7 @@ frameSheet.load(pskl.framesheetData_.content); pskl.app.animationController.setFPS(pskl.framesheetData_.fps); } - this.finishInit(); } - - var drawingLoop = new pskl.rendering.DrawingLoop(); - drawingLoop.addCallback(this.render, this); - drawingLoop.start(); - - // Init (event-delegated) bootstrap tooltips: - $('body').tooltip({ - selector : '[rel=tooltip]' - }); }, render : function (delta) { @@ -91,15 +97,7 @@ this.animationController.render(delta); this.previewsController.render(delta); }, - - finishInit : function () { - var toolController = new pskl.controller.ToolController(); - toolController.init(); - - var paletteController = new pskl.controller.PaletteController(); - paletteController.init(frameSheet); - }, - + readSizeFromURL_ : function () { var sizeParam = this.readUrlParameter_("size"), size; @@ -130,7 +128,7 @@ for (i = 0; i < params.length; i++) { val = params[i].split("="); if (val[0] == paramName) { - return unescape(val[1]); + return window.unescape(val[1]); } } return ""; @@ -146,12 +144,10 @@ frameSheet.load(res.framesheet); pskl.app.animationController.setFPS(res.fps); $.publish(Events.HIDE_NOTIFICATION); - pskl.app.finishInit(); }; xhr.onerror = function () { $.publish(Events.HIDE_NOTIFICATION); - pskl.app.finishInit(); }; xhr.send(); @@ -191,7 +187,7 @@ if (this.status == 200) { if (pskl.app.isStaticVersion) { var baseUrl = window.location.href.replace(window.location.search, ""); - window.location.href = baseUrl + "?frameId=" + this.responseText; + window.location.href = baseUrl + "?frameId=" + this.responseText; } else { $.publish(Events.SHOW_NOTIFICATION, [{"content": "Successfully saved !"}]); } diff --git a/js/rendering/CanvasRenderer.js b/js/rendering/CanvasRenderer.js index fb796798..1a9f94d8 100644 --- a/js/rendering/CanvasRenderer.js +++ b/js/rendering/CanvasRenderer.js @@ -1,36 +1,36 @@ (function () { - var ns = $.namespace("pskl.rendering"); - ns.CanvasRenderer = function (frame, dpi) { - this.frame = frame; - this.dpi = dpi; - }; + var ns = $.namespace("pskl.rendering"); + ns.CanvasRenderer = function (frame, dpi) { + this.frame = frame; + this.dpi = dpi; + }; - ns.CanvasRenderer.prototype.render = function (frame, dpi) { - var canvas = this.createCanvas_(); - var context = canvas.getContext('2d'); - for(var col = 0, width = this.frame.getWidth(); col < width; col++) { - for(var row = 0, height = this.frame.getHeight(); row < height; row++) { - var color = this.frame.getPixel(col, row); - this.renderPixel_(color, col, row, context); - } - } + ns.CanvasRenderer.prototype.render = function (frame, dpi) { + var canvas = this.createCanvas_(); + var context = canvas.getContext('2d'); + for(var col = 0, width = this.frame.getWidth(); col < width; col++) { + for(var row = 0, height = this.frame.getHeight(); row < height; row++) { + var color = this.frame.getPixel(col, row); + this.renderPixel_(color, col, row, context); + } + } - return context; - }; + return context; + }; - ns.CanvasRenderer.prototype.renderPixel_ = function (color, col, row, context) { - if(color == Constants.TRANSPARENT_COLOR) { - color = "#FFF"; - } + ns.CanvasRenderer.prototype.renderPixel_ = function (color, col, row, context) { + if(color == Constants.TRANSPARENT_COLOR) { + color = "#FFF"; + } - context.fillStyle = color; - context.fillRect(col * this.dpi, row * this.dpi, this.dpi, this.dpi); - }; + context.fillStyle = color; + context.fillRect(col * this.dpi, row * this.dpi, this.dpi, this.dpi); + }; - ns.CanvasRenderer.prototype.createCanvas_ = function () { - var width = this.frame.getWidth() * this.dpi; - var height = this.frame.getHeight() * this.dpi; - return pskl.CanvasUtils.createCanvas(width, height); - }; + ns.CanvasRenderer.prototype.createCanvas_ = function () { + var width = this.frame.getWidth() * this.dpi; + var height = this.frame.getHeight() * this.dpi; + return pskl.CanvasUtils.createCanvas(width, height); + }; })(); \ No newline at end of file diff --git a/js/rendering/DrawingLoop.js b/js/rendering/DrawingLoop.js index 672eb3de..88afc149 100644 --- a/js/rendering/DrawingLoop.js +++ b/js/rendering/DrawingLoop.js @@ -1,61 +1,61 @@ (function () { - var ns = $.namespace("pskl.rendering"); + var ns = $.namespace("pskl.rendering"); - ns.DrawingLoop = function () { - this.requestAnimationFrame = this.getRequestAnimationFrameShim_(); - this.isRunning = false; - this.previousTime = 0; - this.callbacks = []; - }; + ns.DrawingLoop = function () { + this.requestAnimationFrame = this.getRequestAnimationFrameShim_(); + this.isRunning = false; + this.previousTime = 0; + this.callbacks = []; + }; - ns.DrawingLoop.prototype.addCallback = function (callback, scope, args) { - var callbackObj = { - fn : callback, - scope : scope, - args : args - }; - this.callbacks.push(callbackObj); - return callbackObj; - }; + ns.DrawingLoop.prototype.addCallback = function (callback, scope, args) { + var callbackObj = { + fn : callback, + scope : scope, + args : args + }; + this.callbacks.push(callbackObj); + return callbackObj; + }; - ns.DrawingLoop.prototype.removeCallback = function (callbackObj) { - var index = this.callbacks.indexOf(callbackObj); - if (index != -1) { - this.callbacks.splice(index, 1); - } - }; + ns.DrawingLoop.prototype.removeCallback = function (callbackObj) { + var index = this.callbacks.indexOf(callbackObj); + if (index != -1) { + this.callbacks.splice(index, 1); + } + }; - ns.DrawingLoop.prototype.start = function () { - this.isRunning = true; - this.loop_(); - }; + ns.DrawingLoop.prototype.start = function () { + this.isRunning = true; + this.loop_(); + }; - ns.DrawingLoop.prototype.loop_ = function () { - var currentTime = Date.now(); - var delta = currentTime - this.previousTime; - this.executeCallbacks_(delta); - this.previousTime = currentTime; - this.requestAnimationFrame.call(window, this.loop_.bind(this)); - }; + ns.DrawingLoop.prototype.loop_ = function () { + var currentTime = Date.now(); + var delta = currentTime - this.previousTime; + this.executeCallbacks_(delta); + this.previousTime = currentTime; + this.requestAnimationFrame.call(window, this.loop_.bind(this)); + }; - ns.DrawingLoop.prototype.executeCallbacks_ = function (deltaTime) { - for (var i = 0 ; i < this.callbacks.length ; i++) { - var cb = this.callbacks[i]; - cb.fn.call(cb.scope, deltaTime, cb.args); - } - }; + ns.DrawingLoop.prototype.executeCallbacks_ = function (deltaTime) { + for (var i = 0 ; i < this.callbacks.length ; i++) { + var cb = this.callbacks[i]; + cb.fn.call(cb.scope, deltaTime, cb.args); + } + }; - ns.DrawingLoop.prototype.stop = function () { - this.isRunning = false; - }; + ns.DrawingLoop.prototype.stop = function () { + this.isRunning = false; + }; - ns.DrawingLoop.prototype.getRequestAnimationFrameShim_ = function () { - var requestAnimationFrame = window.requestAnimationFrame || - window.mozRequestAnimationFrame || - window.webkitRequestAnimationFrame || - window.msRequestAnimationFrame || - function (callback) { window.setTimeout(callback, 1000/60); }; + ns.DrawingLoop.prototype.getRequestAnimationFrameShim_ = function () { + var requestAnimationFrame = window.requestAnimationFrame || + window.mozRequestAnimationFrame || + window.webkitRequestAnimationFrame || + window.msRequestAnimationFrame || + function (callback) { window.setTimeout(callback, 1000/60); }; - return requestAnimationFrame; - }; + return requestAnimationFrame; + }; })(); \ No newline at end of file diff --git a/js/rendering/FrameRenderer.js b/js/rendering/FrameRenderer.js index 99d30131..af5d348f 100644 --- a/js/rendering/FrameRenderer.js +++ b/js/rendering/FrameRenderer.js @@ -1,165 +1,160 @@ (function () { - var ns = $.namespace("pskl.rendering"); + var ns = $.namespace("pskl.rendering"); - ns.FrameRenderer = function (container, renderingOptions, className) { - this.defaultRenderingOptions = { - 'supportGridRendering' : false - }; - renderingOptions = $.extend(true, {}, this.defaultRenderingOptions, renderingOptions); + ns.FrameRenderer = function (container, renderingOptions, className) { + this.defaultRenderingOptions = { + 'supportGridRendering' : false + }; + renderingOptions = $.extend(true, {}, this.defaultRenderingOptions, renderingOptions); - if(container === undefined) { - throw 'Bad FrameRenderer initialization. undefined.'; - } + if(container === undefined) { + throw 'Bad FrameRenderer initialization. undefined.'; + } + + if(isNaN(renderingOptions.dpi)) { + throw 'Bad FrameRenderer initialization. not well defined.'; + } + + this.container = container; + this.dpi = renderingOptions.dpi; + this.className = className; + this.canvas = null; + this.supportGridRendering = renderingOptions.supportGridRendering; + + this.enableGrid(pskl.UserSettings.get(pskl.UserSettings.SHOW_GRID)); + + // Flag to know if the config was altered + this.canvasConfigDirty = true; + this.updateBackgroundClass_(pskl.UserSettings.get(pskl.UserSettings.CANVAS_BACKGROUND)); + $.subscribe(Events.USER_SETTINGS_CHANGED, $.proxy(this.onUserSettingsChange_, this)); + }; + + ns.FrameRenderer.prototype.updateDPI = function (newDPI) { + this.dpi = newDPI; + this.canvasConfigDirty = true; + }; + + /** + * @private + */ + ns.FrameRenderer.prototype.onUserSettingsChange_ = function (evt, settingName, settingValue) { + + if(settingName == pskl.UserSettings.SHOW_GRID) { + this.enableGrid(settingValue); + } + else if (settingName == pskl.UserSettings.CANVAS_BACKGROUND) { + this.updateBackgroundClass_(settingValue); + } + }; + + /** + * @private + */ + ns.FrameRenderer.prototype.updateBackgroundClass_ = function (newClass) { + var currentClass = this.container.data('current-background-class'); + if (currentClass) { + this.container.removeClass(currentClass); + } + this.container.addClass(newClass); + this.container.data('current-background-class', newClass); + }; + + ns.FrameRenderer.prototype.enableGrid = function (flag) { + this.gridStrokeWidth = (flag && this.supportGridRendering) ? Constants.GRID_STROKE_WIDTH : 0; + this.canvasConfigDirty = true; + }; + + ns.FrameRenderer.prototype.render = function (frame) { + this.clear(frame); + var context = this.getCanvas_(frame).getContext('2d'); + for(var col = 0, width = frame.getWidth(); col < width; col++) { + for(var row = 0, height = frame.getHeight(); row < height; row++) { + var color = frame.getPixel(col, row); + this.renderPixel_(color, col, row, context); + } + } + this.lastRenderedFrame = frame; + }; + + ns.FrameRenderer.prototype.renderPixel_ = function (color, col, row, context) { + if(color != Constants.TRANSPARENT_COLOR) { + context.fillStyle = color; + context.fillRect(this.getFramePos_(col), this.getFramePos_(row), this.dpi, this.dpi); + } + }; + + ns.FrameRenderer.prototype.clear = function (frame) { + var canvas = this.getCanvas_(frame); + canvas.getContext("2d").clearRect(0, 0, canvas.width, canvas.height); + }; + + /** + * Transform a screen pixel-based coordinate (relative to the top-left corner of the rendered + * frame) into a sprite coordinate in column and row. + * @public + */ + ns.FrameRenderer.prototype.convertPixelCoordinatesIntoSpriteCoordinate = function(coords) { + var cellSize = this.dpi + this.gridStrokeWidth; + return { + "col" : (coords.x - coords.x % cellSize) / cellSize, + "row" : (coords.y - coords.y % cellSize) / cellSize + }; + }; + + /** + * @private + */ + ns.FrameRenderer.prototype.getFramePos_ = function(index) { + return index * this.dpi + ((index - 1) * this.gridStrokeWidth); + }; + + /** + * @private + */ + ns.FrameRenderer.prototype.drawGrid_ = function(canvas, width, height, col, row) { + var ctx = canvas.getContext("2d"); + ctx.lineWidth = Constants.GRID_STROKE_WIDTH; + ctx.strokeStyle = Constants.GRID_STROKE_COLOR; + for(var c=1; c < col; c++) { + ctx.moveTo(this.getFramePos_(c), 0); + ctx.lineTo(this.getFramePos_(c), height); + ctx.stroke(); + } + + for(var r=1; r < row; r++) { + ctx.moveTo(0, this.getFramePos_(r)); + ctx.lineTo(width, this.getFramePos_(r)); + ctx.stroke(); + } + }; + + /** + * @private + */ + ns.FrameRenderer.prototype.getCanvas_ = function (frame) { + if(this.canvasConfigDirty) { + $(this.canvas).remove(); + + var col = frame.getWidth(), + row = frame.getHeight(); + + var pixelWidth = col * this.dpi + this.gridStrokeWidth * (col - 1); + var pixelHeight = row * this.dpi + this.gridStrokeWidth * (row - 1); + var classes = ['canvas']; + if (this.className) { + classes.push(this.className); + } + var canvas = pskl.CanvasUtils.createCanvas(pixelWidth, pixelHeight, classes); + + this.container.append(canvas); + + if(this.gridStrokeWidth > 0) { + this.drawGrid_(canvas, pixelWidth, pixelHeight, col, row); + } - if(isNaN(renderingOptions.dpi)) { - throw 'Bad FrameRenderer initialization. not well defined.'; - } - - this.container = container; - this.dpi = renderingOptions.dpi; - this.className = className; - this.canvas = null; - this.supportGridRendering = renderingOptions.supportGridRendering; - - this.enableGrid(pskl.UserSettings.get(pskl.UserSettings.SHOW_GRID)); - - // Flag to know if the config was altered - this.canvasConfigDirty = true; - this.updateBackgroundClass_(pskl.UserSettings.get(pskl.UserSettings.CANVAS_BACKGROUND)); - $.subscribe(Events.USER_SETTINGS_CHANGED, $.proxy(this.onUserSettingsChange_, this)); - }; - - ns.FrameRenderer.prototype.init = function (frame) { - this.render(frame); - this.lastRenderedFrame = frame; - }; - - ns.FrameRenderer.prototype.updateDPI = function (newDPI) { - this.dpi = newDPI; - this.canvasConfigDirty = true; - }; - - /** - * @private - */ - ns.FrameRenderer.prototype.onUserSettingsChange_ = function (evt, settingName, settingValue) { - - if(settingName == pskl.UserSettings.SHOW_GRID) { - this.enableGrid(settingValue); - } - else if (settingName == pskl.UserSettings.CANVAS_BACKGROUND) { - this.updateBackgroundClass_(settingValue); - } - }; - - /** - * @private - */ - ns.FrameRenderer.prototype.updateBackgroundClass_ = function (newClass) { - var currentClass = this.container.data('current-background-class'); - if (currentClass) { - this.container.removeClass(currentClass); - } - this.container.addClass(newClass); - this.container.data('current-background-class', newClass); - }; - - ns.FrameRenderer.prototype.enableGrid = function (flag) { - this.gridStrokeWidth = (flag && this.supportGridRendering) ? Constants.GRID_STROKE_WIDTH : 0; - this.canvasConfigDirty = true; - }; - - ns.FrameRenderer.prototype.render = function (frame) { - this.clear(frame); - var context = this.getCanvas_(frame).getContext('2d'); - for(var col = 0, width = frame.getWidth(); col < width; col++) { - for(var row = 0, height = frame.getHeight(); row < height; row++) { - var color = frame.getPixel(col, row); - this.renderPixel_(color, col, row, context); - } - } - this.lastRenderedFrame = frame; - }; - - ns.FrameRenderer.prototype.renderPixel_ = function (color, col, row, context) { - if(color != Constants.TRANSPARENT_COLOR) { - context.fillStyle = color; - context.fillRect(this.getFramePos_(col), this.getFramePos_(row), this.dpi, this.dpi); - } - }; - - ns.FrameRenderer.prototype.clear = function (frame) { - var canvas = this.getCanvas_(frame); - canvas.getContext("2d").clearRect(0, 0, canvas.width, canvas.height); - }; - - /** - * Transform a screen pixel-based coordinate (relative to the top-left corner of the rendered - * frame) into a sprite coordinate in column and row. - * @public - */ - ns.FrameRenderer.prototype.convertPixelCoordinatesIntoSpriteCoordinate = function(coords) { - var cellSize = this.dpi + this.gridStrokeWidth; - return { - "col" : (coords.x - coords.x % cellSize) / cellSize, - "row" : (coords.y - coords.y % cellSize) / cellSize - }; - }; - - /** - * @private - */ - ns.FrameRenderer.prototype.getFramePos_ = function(index) { - return index * this.dpi + ((index - 1) * this.gridStrokeWidth); - }; - - /** - * @private - */ - ns.FrameRenderer.prototype.drawGrid_ = function(canvas, width, height, col, row) { - var ctx = canvas.getContext("2d"); - ctx.lineWidth = Constants.GRID_STROKE_WIDTH; - ctx.strokeStyle = Constants.GRID_STROKE_COLOR; - for(var c=1; c < col; c++) { - ctx.moveTo(this.getFramePos_(c), 0); - ctx.lineTo(this.getFramePos_(c), height); - ctx.stroke(); - } - - for(var r=1; r < row; r++) { - ctx.moveTo(0, this.getFramePos_(r)); - ctx.lineTo(width, this.getFramePos_(r)); - ctx.stroke(); - } - }; - - /** - * @private - */ - ns.FrameRenderer.prototype.getCanvas_ = function (frame) { - if(this.canvasConfigDirty) { - $(this.canvas).remove(); - - var col = frame.getWidth(), - row = frame.getHeight(); - - var pixelWidth = col * this.dpi + this.gridStrokeWidth * (col - 1); - var pixelHeight = row * this.dpi + this.gridStrokeWidth * (row - 1); - var classes = ['canvas']; - if (this.className) { - classes.push(this.className); - } - var canvas = pskl.CanvasUtils.createCanvas(pixelWidth, pixelHeight, classes); - - this.container.append(canvas); - - if(this.gridStrokeWidth > 0) { - this.drawGrid_(canvas, pixelWidth, pixelHeight, col, row); - } - - this.canvas = canvas; - this.canvasConfigDirty = false; - } - return this.canvas; - }; + this.canvas = canvas; + this.canvasConfigDirty = false; + } + return this.canvas; + }; })(); \ No newline at end of file diff --git a/js/rendering/SpritesheetRenderer.js b/js/rendering/SpritesheetRenderer.js index f7e61fd5..b7b4c056 100644 --- a/js/rendering/SpritesheetRenderer.js +++ b/js/rendering/SpritesheetRenderer.js @@ -1,63 +1,63 @@ (function () { - var ns = $.namespace("pskl.rendering"); + var ns = $.namespace("pskl.rendering"); - ns.SpritesheetRenderer = function (framesheet) { - this.framesheet = framesheet; - }; + ns.SpritesheetRenderer = function (framesheet) { + this.framesheet = framesheet; + }; - ns.SpritesheetRenderer.prototype.renderAsImageDataSpritesheetPNG = function () { - var canvas = this.createCanvas_(); - for (var i = 0 ; i < this.framesheet.getFrameCount() ; i++) { - var frame = this.framesheet.getFrameByIndex(i); - this.drawFrameInCanvas_(frame, canvas, i * this.framesheet.getWidth(), 0); - } - return canvas.toDataURL("image/png"); - }; + ns.SpritesheetRenderer.prototype.renderAsImageDataSpritesheetPNG = function () { + var canvas = this.createCanvas_(); + for (var i = 0 ; i < this.framesheet.getFrameCount() ; i++) { + var frame = this.framesheet.getFrameByIndex(i); + this.drawFrameInCanvas_(frame, canvas, i * this.framesheet.getWidth(), 0); + } + return canvas.toDataURL("image/png"); + }; - ns.SpritesheetRenderer.prototype.renderAsImageDataAnimatedGIF = function (fps) { - var encoder = new GIFEncoder(), dpi = 10; - encoder.setRepeat(0); - encoder.setDelay(1000/fps); + ns.SpritesheetRenderer.prototype.renderAsImageDataAnimatedGIF = function (fps) { + var encoder = new window.GIFEncoder(), dpi = 10; + encoder.setRepeat(0); + encoder.setDelay(1000/fps); - encoder.start(); - encoder.setSize(this.framesheet.getWidth() * dpi, this.framesheet.getHeight() * dpi); - for (var i = 0 ; i < this.framesheet.frames.length ; i++) { - var frame = this.framesheet.frames[i]; - var renderer = new pskl.rendering.CanvasRenderer(frame, dpi); - encoder.addFrame(renderer.render()); + encoder.start(); + encoder.setSize(this.framesheet.getWidth() * dpi, this.framesheet.getHeight() * dpi); + for (var i = 0 ; i < this.framesheet.frames.length ; i++) { + var frame = this.framesheet.frames[i]; + var renderer = new pskl.rendering.CanvasRenderer(frame, dpi); + encoder.addFrame(renderer.render()); + } + encoder.finish(); + + var imageData = 'data:image/gif;base64,' + window.encode64(encoder.stream().getData()); + return imageData; + }; + + + /** + * TODO(juliandescottes): Mutualize with code already present in FrameRenderer + */ + ns.SpritesheetRenderer.prototype.drawFrameInCanvas_ = function (frame, canvas, offsetWidth, offsetHeight) { + var context = canvas.getContext('2d'); + for(var col = 0, width = frame.getWidth(); col < width; col++) { + for(var row = 0, height = frame.getHeight(); row < height; row++) { + var color = frame.getPixel(col, row); + if(color != Constants.TRANSPARENT_COLOR) { + context.fillStyle = color; + context.fillRect(col + offsetWidth, row + offsetHeight, 1, 1); } - encoder.finish(); + } + } + }; - var imageData = 'data:image/gif;base64,' + encode64(encoder.stream().getData()); - return imageData; - }; - - - /** - * TODO(juliandescottes): Mutualize with code already present in FrameRenderer - */ - ns.SpritesheetRenderer.prototype.drawFrameInCanvas_ = function (frame, canvas, offsetWidth, offsetHeight) { - var context = canvas.getContext('2d'); - for(var col = 0, width = frame.getWidth(); col < width; col++) { - for(var row = 0, height = frame.getHeight(); row < height; row++) { - var color = frame.getPixel(col, row); - if(color != Constants.TRANSPARENT_COLOR) { - context.fillStyle = color; - context.fillRect(col + offsetWidth, row + offsetHeight, 1, 1); - } - } - } - }; - - ns.SpritesheetRenderer.prototype.createCanvas_ = function () { - var frameCount = this.framesheet.getFrameCount(); - if (frameCount > 0){ - var width = frameCount * this.framesheet.getWidth(); - var height = this.framesheet.getHeight(); - return pskl.CanvasUtils.createCanvas(width, height); - } else { - throw "Cannot render empty Spritesheet"; - } - }; + ns.SpritesheetRenderer.prototype.createCanvas_ = function () { + var frameCount = this.framesheet.getFrameCount(); + if (frameCount > 0){ + var width = frameCount * this.framesheet.getWidth(); + var height = this.framesheet.getHeight(); + return pskl.CanvasUtils.createCanvas(width, height); + } else { + throw "Cannot render empty Spritesheet"; + } + }; })(); \ No newline at end of file diff --git a/js/selection/BaseSelection.js b/js/selection/BaseSelection.js index 82583445..74513b58 100644 --- a/js/selection/BaseSelection.js +++ b/js/selection/BaseSelection.js @@ -1,34 +1,34 @@ (function () { - var ns = $.namespace("pskl.selection"); + var ns = $.namespace("pskl.selection"); - ns.BaseSelection = function () { - this.reset(); - }; + ns.BaseSelection = function () { + this.reset(); + }; - ns.BaseSelection.prototype.reset = function () { - this.pixels = []; - this.hasPastedContent = false; - }; + ns.BaseSelection.prototype.reset = function () { + this.pixels = []; + this.hasPastedContent = false; + }; - ns.BaseSelection.prototype.move = function (colDiff, rowDiff) { - var movedPixel, movedPixels = []; + ns.BaseSelection.prototype.move = function (colDiff, rowDiff) { + var movedPixel, movedPixels = []; - for(var i=0, l=this.pixels.length; i"; + throw "Bad LocalStorageService initialization: "; } this.framesheet = framesheet_; this.localStorageThrottler_ = null; diff --git a/js/utils/CanvasUtils.js b/js/utils/CanvasUtils.js index ae68b43b..dd03612b 100644 --- a/js/utils/CanvasUtils.js +++ b/js/utils/CanvasUtils.js @@ -1,22 +1,22 @@ (function () { - var ns = $.namespace("pskl"); + var ns = $.namespace("pskl"); - ns.CanvasUtils = { - createCanvas : function (width, height, classList) { - var canvas = document.createElement("canvas"); - canvas.setAttribute("width", width); - canvas.setAttribute("height", height); + ns.CanvasUtils = { + createCanvas : function (width, height, classList) { + var canvas = document.createElement("canvas"); + canvas.setAttribute("width", width); + canvas.setAttribute("height", height); - if (typeof classList == "string") { - classList = [classList]; - } - if (Array.isArray(classList)) { - for (var i = 0 ; i < classList.length ; i++) { - canvas.classList.add(classList[i]); - } - } - - return canvas; - } - }; + if (typeof classList == "string") { + classList = [classList]; + } + if (Array.isArray(classList)) { + for (var i = 0 ; i < classList.length ; i++) { + canvas.classList.add(classList[i]); + } + } + + return canvas; + } + }; })(); \ No newline at end of file diff --git a/js/utils/PixelUtils.js b/js/utils/PixelUtils.js index f26d77d3..94e1fcb5 100644 --- a/js/utils/PixelUtils.js +++ b/js/utils/PixelUtils.js @@ -1,175 +1,177 @@ (function () { - var ns = $.namespace("pskl"); + var ns = $.namespace("pskl"); - ns.PixelUtils = { + ns.PixelUtils = { - getRectanglePixels : function (x0, y0, x1, y1) { - var rectangle = this.getOrderedRectangleCoordinates(x0, y0, x1, y1); - var pixels = []; + getRectanglePixels : function (x0, y0, x1, y1) { + var rectangle = this.getOrderedRectangleCoordinates(x0, y0, x1, y1); + var pixels = []; - for(var x = rectangle.x0; x <= rectangle.x1; x++) { - for(var y = rectangle.y0; y <= rectangle.y1; y++) { - pixels.push({"col": x, "row": y}); - } + for(var x = rectangle.x0; x <= rectangle.x1; x++) { + for(var y = rectangle.y0; y <= rectangle.y1; y++) { + pixels.push({"col": x, "row": y}); + } + } + + return pixels; + }, + + getBoundRectanglePixels : function (x0, y0, x1, y1) { + var rectangle = this.getOrderedRectangleCoordinates(x0, y0, x1, y1); + var pixels = []; + // Creating horizontal sides of the rectangle: + for(var x = rectangle.x0; x <= rectangle.x1; x++) { + pixels.push({"col": x, "row": rectangle.y0}); + pixels.push({"col": x, "row": rectangle.y1}); + } + + // Creating vertical sides of the rectangle: + for(var y = rectangle.y0; y <= rectangle.y1; y++) { + pixels.push({"col": rectangle.x0, "row": y}); + pixels.push({"col": rectangle.x1, "row": y}); + } + + return pixels; + }, + + /** + * Return an object of ordered rectangle coordinate. + * In returned object {x0, y0} => top left corner - {x1, y1} => bottom right corner + * @private + */ + getOrderedRectangleCoordinates : function (x0, y0, x1, y1) { + return { + x0 : Math.min(x0, x1), + y0 : Math.min(y0, y1), + x1 : Math.max(x0, x1), + y1 : Math.max(y0, y1), + }; + }, + + /** + * Return the list of pixels that would have been filled by a paintbucket tool applied + * on pixel at coordinate (x,y). + * This function is not altering the Frame object argument. + * + * @param frame pskl.model.Frame The frame target in which we want to paintbucket + * @param col number Column coordinate in the frame + * @param row number Row coordinate in the frame + * + * @return an array of the pixel coordinates paint with the replacement color + */ + getSimilarConnectedPixelsFromFrame: function(frame, col, row) { + // To get the list of connected (eg the same color) pixels, we will use the paintbucket algorithm + // in a fake cloned frame. The returned pixels by the paintbucket algo are the painted pixels + // and are as well connected. + var fakeFrame = frame.clone(); // We just want to + var fakeFillColor = "sdfsdfsdf"; // A fake color that will never match a real color. + var paintedPixels = this.paintSimilarConnectedPixelsFromFrame(fakeFrame, col, row, fakeFillColor); + + return paintedPixels; + }, + + /** + * Apply the paintbucket tool in a frame at the (col, row) initial position + * with the replacement color. + * + * @param frame pskl.model.Frame The frame target in which we want to paintbucket + * @param col number Column coordinate in the frame + * @param row number Row coordinate in the frame + * @param replacementColor string Hexadecimal color used to fill the area + * + * @return an array of the pixel coordinates paint with the replacement color + */ + paintSimilarConnectedPixelsFromFrame: function(frame, col, row, replacementColor) { + /** + * Queue linear Flood-fill (node, target-color, replacement-color): + * 1. Set Q to the empty queue. + * 2. If the color of node is not equal to target-color, return. + * 3. Add node to Q. + * 4. For each element n of Q: + * 5. If the color of n is equal to target-color: + * 6. Set w and e equal to n. + * 7. Move w to the west until the color of the node to the west of w no longer matches target-color. + * 8. Move e to the east until the color of the node to the east of e no longer matches target-color. + * 9. Set the color of nodes between w and e to replacement-color. + * 10. For each node n between w and e: + * 11. If the color of the node to the north of n is target-color, add that node to Q. + * 12. If the color of the node to the south of n is target-color, add that node to Q. + * 13. Continue looping until Q is exhausted. + * 14. Return. + */ + var paintedPixels = []; + var queue = []; + var dy = [-1, 0, 1, 0]; + var dx = [0, 1, 0, -1]; + var targetColor; + try { + targetColor = frame.getPixel(col, row); + } catch(e) { + // Frame out of bound exception. + } + + if(targetColor == replacementColor) { + return; + } + + + queue.push({"col": col, "row": row}); + var loopCount = 0; + var cellCount = frame.getWidth() * frame.getHeight(); + while(queue.length > 0) { + loopCount ++; + + 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++) { + var nextCol = currentItem.col + dx[i]; + var nextRow = currentItem.row + dy[i]; + try { + if (frame.containsPixel(nextCol, nextRow) && frame.getPixel(nextCol, nextRow) == targetColor) { + queue.push({"col": nextCol, "row": nextRow }); } - - return pixels; - }, + } catch(e) { + // Frame out of bound exception. + } + } - getBoundRectanglePixels : function (x0, y0, x1, y1) { - var rectangle = this.getOrderedRectangleCoordinates(x0, y0, x1, y1); - var pixels = []; - // Creating horizontal sides of the rectangle: - for(var x = rectangle.x0; x <= rectangle.x1; x++) { - pixels.push({"col": x, "row": rectangle.y0}); - pixels.push({"col": x, "row": rectangle.y1}); - } + // Security loop breaker: + if(loopCount > 10 * cellCount) { + console.log("loop breaker called"); + break; + } + } + return paintedPixels; + }, - // Creating vertical sides of the rectangle: - for(var y = rectangle.y0; y <= rectangle.y1; y++) { - pixels.push({"col": rectangle.x0, "row": y}); - pixels.push({"col": rectangle.x1, "row": y}); - } - - return pixels; - }, + /** + * Calculate and return the maximal DPI to display a picture in a given container. + * + * @param container jQueryObject Container where the picture should be displayed + * @param number pictureHeight height in pixels of the picture to display + * @param number pictureWidth width in pixels of the picture to display + * @return number maximal dpi + */ + calculateDPIForContainer : function (container, pictureHeight, pictureWidth) { + return this.calculateDPI(container.height(), container.width(), pictureHeight, pictureWidth); + }, - /** - * Return an object of ordered rectangle coordinate. - * In returned object {x0, y0} => top left corner - {x1, y1} => bottom right corner - * @private - */ - getOrderedRectangleCoordinates : function (x0, y0, x1, y1) { - return { - x0 : Math.min(x0, x1), y0 : Math.min(y0, y1), - x1 : Math.max(x0, x1), y1 : Math.max(y0, y1), - }; - }, + /** + * Calculate and return the maximal DPI to display a picture for a given height and width. + * + * @param height number Height available to display the picture + * @param width number Width available to display the picture + * @param number pictureHeight height in pixels of the picture to display + * @param number pictureWidth width in pixels of the picture to display + * @return number maximal dpi + */ + calculateDPI : function (height, width, pictureHeight, pictureWidth) { + var heightBoundDpi = Math.floor(height / pictureHeight), + widthBoundDpi = Math.floor(width / pictureWidth); - /** - * Return the list of pixels that would have been filled by a paintbucket tool applied - * on pixel at coordinate (x,y). - * This function is not altering the Frame object argument. - * - * @param frame pskl.model.Frame The frame target in which we want to paintbucket - * @param col number Column coordinate in the frame - * @param row number Row coordinate in the frame - * - * @return an array of the pixel coordinates paint with the replacement color - */ - getSimilarConnectedPixelsFromFrame: function(frame, col, row) { - // To get the list of connected (eg the same color) pixels, we will use the paintbucket algorithm - // in a fake cloned frame. The returned pixels by the paintbucket algo are the painted pixels - // and are as well connected. - var fakeFrame = frame.clone(); // We just want to - var fakeFillColor = "sdfsdfsdf"; // A fake color that will never match a real color. - var paintedPixels = this.paintSimilarConnectedPixelsFromFrame(fakeFrame, col, row, fakeFillColor); - - return paintedPixels; - }, - - /** - * Apply the paintbucket tool in a frame at the (col, row) initial position - * with the replacement color. - * - * @param frame pskl.model.Frame The frame target in which we want to paintbucket - * @param col number Column coordinate in the frame - * @param row number Row coordinate in the frame - * @param replacementColor string Hexadecimal color used to fill the area - * - * @return an array of the pixel coordinates paint with the replacement color - */ - paintSimilarConnectedPixelsFromFrame: function(frame, col, row, replacementColor) { - /** - * Queue linear Flood-fill (node, target-color, replacement-color): - * 1. Set Q to the empty queue. - * 2. If the color of node is not equal to target-color, return. - * 3. Add node to Q. - * 4. For each element n of Q: - * 5. If the color of n is equal to target-color: - * 6. Set w and e equal to n. - * 7. Move w to the west until the color of the node to the west of w no longer matches target-color. - * 8. Move e to the east until the color of the node to the east of e no longer matches target-color. - * 9. Set the color of nodes between w and e to replacement-color. - * 10. For each node n between w and e: - * 11. If the color of the node to the north of n is target-color, add that node to Q. - * 12. If the color of the node to the south of n is target-color, add that node to Q. - * 13. Continue looping until Q is exhausted. - * 14. Return. - */ - var paintedPixels = []; - var queue = []; - var dy = [-1, 0, 1, 0]; - var dx = [0, 1, 0, -1]; - var targetColor; - try { - targetColor = frame.getPixel(col, row); - } catch(e) { - // Frame out of bound exception. - } - - if(targetColor == replacementColor) { - return; - } - - - queue.push({"col": col, "row": row}); - var loopCount = 0; - var cellCount = frame.getWidth() * frame.getHeight(); - while(queue.length > 0) { - loopCount ++; - - 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++) { - var nextCol = currentItem.col + dx[i]; - var nextRow = currentItem.row + dy[i]; - try { - if (frame.containsPixel(nextCol, nextRow) && frame.getPixel(nextCol, nextRow) == targetColor) { - queue.push({"col": nextCol, "row": nextRow }); - } - } catch(e) { - // Frame out of bound exception. - } - } - - // Security loop breaker: - if(loopCount > 10 * cellCount) { - console.log("loop breaker called"); - break; - } - } - return paintedPixels; - }, - - /** - * Calculate and return the maximal DPI to display a picture in a given container. - * - * @param container jQueryObject Container where the picture should be displayed - * @param number pictureHeight height in pixels of the picture to display - * @param number pictureWidth width in pixels of the picture to display - * @return number maximal dpi - */ - calculateDPIForContainer : function (container, pictureHeight, pictureWidth) { - return this.calculateDPI(container.height(), container.width(), pictureHeight, pictureWidth); - }, - - /** - * Calculate and return the maximal DPI to display a picture for a given height and width. - * - * @param height number Height available to display the picture - * @param width number Width available to display the picture - * @param number pictureHeight height in pixels of the picture to display - * @param number pictureWidth width in pixels of the picture to display - * @return number maximal dpi - */ - calculateDPI : function (height, width, pictureHeight, pictureWidth) { - var heightBoundDpi = Math.floor(height / pictureHeight), - widthBoundDpi = Math.floor(width / pictureWidth); - - return Math.min(heightBoundDpi, widthBoundDpi); - }, - }; + return Math.min(heightBoundDpi, widthBoundDpi); + }, + }; })(); \ No newline at end of file diff --git a/js/utils/UserSettings.js b/js/utils/UserSettings.js index 89359bee..7d4a53de 100644 --- a/js/utils/UserSettings.js +++ b/js/utils/UserSettings.js @@ -1,76 +1,76 @@ (function () { - var ns = $.namespace("pskl"); + var ns = $.namespace("pskl"); - ns.UserSettings = { + ns.UserSettings = { - SHOW_GRID : 'SHOW_GRID', - CANVAS_BACKGROUND : 'CANVAS_BACKGROUND', + SHOW_GRID : 'SHOW_GRID', + CANVAS_BACKGROUND : 'CANVAS_BACKGROUND', - KEY_TO_DEFAULT_VALUE_MAP_ : { - 'SHOW_GRID' : false, - 'CANVAS_BACKGROUND' : 'medium-canvas-background' - }, + KEY_TO_DEFAULT_VALUE_MAP_ : { + 'SHOW_GRID' : false, + 'CANVAS_BACKGROUND' : 'medium-canvas-background' + }, - /** - * @private - */ - cache_ : {}, + /** + * @private + */ + cache_ : {}, - /** - * Static method to access a user defined settings value ot its default - * value if not defined yet. - */ - get : function (key) { - this.checkKeyValidity_(key); - if (!(key in this.cache_)) { - this.cache_[key] = - this.readFromLocalStorage_(key) || this.readFromDefaults_(key); - } - return this.cache_[key]; - }, + /** + * Static method to access a user defined settings value ot its default + * value if not defined yet. + */ + get : function (key) { + this.checkKeyValidity_(key); + if (!(key in this.cache_)) { + this.cache_[key] = + this.readFromLocalStorage_(key) || this.readFromDefaults_(key); + } + return this.cache_[key]; + }, - set : function (key, value) { - this.checkKeyValidity_(key); - this.cache_[key] = value; - this.writeToLocalStorage_(key, value); + set : function (key, value) { + this.checkKeyValidity_(key); + this.cache_[key] = value; + this.writeToLocalStorage_(key, value); - $.publish(Events.USER_SETTINGS_CHANGED, [key, value]); - }, + $.publish(Events.USER_SETTINGS_CHANGED, [key, value]); + }, - /** - * @private - */ - readFromLocalStorage_ : function(key) { - var value = window.localStorage[key]; - if (typeof value != "undefined") { - value = JSON.parse(value); - } - return value; - }, + /** + * @private + */ + readFromLocalStorage_ : function(key) { + var value = window.localStorage[key]; + if (typeof value != "undefined") { + value = JSON.parse(value); + } + return value; + }, - /** - * @private - */ - writeToLocalStorage_ : function(key, value) { - // TODO(grosbouddha): Catch storage exception here. - window.localStorage[key] = JSON.stringify(value); - }, + /** + * @private + */ + writeToLocalStorage_ : function(key, value) { + // TODO(grosbouddha): Catch storage exception here. + window.localStorage[key] = JSON.stringify(value); + }, - /** - * @private - */ - readFromDefaults_ : function (key) { - return this.KEY_TO_DEFAULT_VALUE_MAP_[key]; - }, + /** + * @private + */ + readFromDefaults_ : function (key) { + return this.KEY_TO_DEFAULT_VALUE_MAP_[key]; + }, - /** - * @private - */ - checkKeyValidity_ : function(key) { - if(!(key in this.KEY_TO_DEFAULT_VALUE_MAP_)) { - // TODO(grosbouddha): Define error catching strategy and throw exception from here. - console.log("UserSettings key <"+ key +"> not find in supported keys."); - } - } - }; + /** + * @private + */ + checkKeyValidity_ : function(key) { + if(!(key in this.KEY_TO_DEFAULT_VALUE_MAP_)) { + // TODO(grosbouddha): Define error catching strategy and throw exception from here. + console.log("UserSettings key <"+ key +"> not find in supported keys."); + } + } + }; })(); \ No newline at end of file diff --git a/js/utils/core.js b/js/utils/core.js index 1bc1c8ef..58fc39fd 100644 --- a/js/utils/core.js +++ b/js/utils/core.js @@ -1,27 +1,27 @@ jQuery.namespace = function() { - var a=arguments, o=null, i, j, d; - for (i=0; i 255 || g > 255 || b > 255) - throw "Invalid color component"; - return ((r << 16) | (g << 8) | b).toString(16); - }; + ns.rgbToHex = function(r, g, b) { + if (r > 255 || g > 255 || b > 255) + throw "Invalid color component"; + return ((r << 16) | (g << 8) | b).toString(16); + }; - ns.inherit = function(extendedObject, inheritFrom) { - extendedObject.prototype = Object.create(inheritFrom.prototype); - extendedObject.prototype.constructor = extendedObject; - extendedObject.prototype.superclass = inheritFrom.prototype; - }; + ns.inherit = function(extendedObject, inheritFrom) { + extendedObject.prototype = Object.create(inheritFrom.prototype); + extendedObject.prototype.constructor = extendedObject; + extendedObject.prototype.superclass = inheritFrom.prototype; + }; })(); diff --git a/package.json b/package.json index b48a3a5f..bba2234a 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "grunt-contrib-connect": "0.3.0", "grunt-contrib-jshint": "0.5.4", "grunt-contrib-uglify": "0.2.2", + "grunt-contrib-concat": "0.1.2", "grunt-ghost": "1.0.12", - "grunt-contrib-concat": "0.1.2" + "grunt-leading-indent" : "0.1.0" } }