Dev environment:force indentation to 2 spaces. Added new grunt module, grunt-leading-indent to check space consistency, and modified jshint options to enforce 2 spaces

This commit is contained in:
jdescottes
2013-08-10 12:11:16 +02:00
parent 9a68da8bda
commit 87a68bfe21
40 changed files with 2222 additions and 2206 deletions

View File

@ -5,56 +5,69 @@
* - run a grunt target defined in Gruntfiles.js, ex: 'grunt lint' * - run a grunt target defined in Gruntfiles.js, ex: 'grunt lint'
* *
* Note: The 'ghost' grunt task have special deps on CasperJS and phantomjs. * Note: The 'ghost' grunt task have special deps on CasperJS and phantomjs.
* For now, It's configured to run only on TravisCI where these deps are * For now, It's configured to run only on TravisCI where these deps are
* correctly defined. * correctly defined.
* If you run this task locally, it may require some env set up first. * If you run this task locally, it may require some env set up first.
*/ */
module.exports = function(grunt) { module.exports = function(grunt) {
grunt.initConfig({ grunt.initConfig({
jshint: { jshint: {
/*options: { /*options: {
"evil": true, "evil": true,
"asi": true, "asi": true,
"smarttabs": true, "smarttabs": true,
"eqnull": true "eqnull": true
},*/ },*/
files: [ options: {
'Gruntfile.js', indent:2
'package.json', },
'js/**/*.js', files: [
'!js/lib/**/*.js' // Exclude lib folder (note the leading !) 'Gruntfile.js',
] 'package.json',
}, 'js/**/*.js',
connect: { '!js/lib/**/*.js' // Exclude lib folder (note the leading !)
www: { ]
options: { },
base: '.', connect: {
port: 4545 www: {
} options: {
} base: '.',
}, port: 4545
ghost: { }
dist: { }
filesSrc: ['tests/integration/casperjs/*_test.js'], },
options: { ghost: {
args: { dist: {
baseUrl: 'http://localhost:' + filesSrc: ['tests/integration/casperjs/*_test.js'],
'<%= connect.www.options.port %>/' options: {
}, args: {
direct: false, baseUrl: 'http://localhost:' + '<%= connect.www.options.port %>/'
logLevel: 'error', },
printCommand: false, direct: false,
printFilePaths: true logLevel: 'error',
} printCommand: false,
} printFilePaths: true
} }
}); }
}
});
grunt.loadNpmTasks('grunt-contrib-connect'); grunt.config.set('leadingIndent.indentation', 'spaces');
grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.config.set('leadingIndent.jsFiles', {
grunt.loadNpmTasks('grunt-ghost'); src: ['js/**/*.js','!js/lib/**/*.js']
});
grunt.config.set('leadingIndent.cssFiles', {
src: ['css/**/*.css']
});
grunt.registerTask('lint', ['jshint']);
grunt.registerTask('test', ['jshint', 'connect', 'ghost']); grunt.loadNpmTasks('grunt-leading-indent');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-ghost');
grunt.registerTask('check-indent', ['leadingIndent:jsFiles', 'leadingIndent:cssFiles']);
grunt.registerTask('lint', ['jshint']);
grunt.registerTask('test', ['leadingIndent:jsFiles', 'leadingIndent:cssFiles', 'jshint', 'connect', 'ghost']);
}; };

View File

@ -1,36 +1,36 @@
// TODO(grosbouddha): put under pskl namespace. // TODO(grosbouddha): put under pskl namespace.
var Constants = { var Constants = {
DEFAULT_SIZE : { DEFAULT_SIZE : {
height : 32, height : 32,
width : 32 width : 32
}, },
MAX_HEIGHT : 128, MAX_HEIGHT : 128,
MAX_WIDTH : 128, MAX_WIDTH : 128,
DEFAULT_PEN_COLOR : '#000000', DEFAULT_PEN_COLOR : '#000000',
TRANSPARENT_COLOR : 'TRANSPARENT', TRANSPARENT_COLOR : 'TRANSPARENT',
/* /*
* Fake semi-transparent color used to highlight transparent * Fake semi-transparent color used to highlight transparent
* strokes and rectangles: * strokes and rectangles:
*/ */
SELECTION_TRANSPARENT_COLOR: 'rgba(255, 255, 255, 0.6)', SELECTION_TRANSPARENT_COLOR: 'rgba(255, 255, 255, 0.6)',
/* /*
* When a tool is hovering the drawing canvas, we highlight the eventual * When a tool is hovering the drawing canvas, we highlight the eventual
* pixel target with this color: * pixel target with this color:
*/ */
TOOL_TARGET_HIGHLIGHT_COLOR: 'rgba(255, 255, 255, 0.2)', TOOL_TARGET_HIGHLIGHT_COLOR: 'rgba(255, 255, 255, 0.2)',
/* /*
* Default entry point for piskel web service: * Default entry point for piskel web service:
*/ */
PISKEL_SERVICE_URL: 'http://3.piskel-app.appspot.com', PISKEL_SERVICE_URL: 'http://3.piskel-app.appspot.com',
GRID_STROKE_WIDTH: 1, GRID_STROKE_WIDTH: 1,
GRID_STROKE_COLOR: "lightgray", GRID_STROKE_COLOR: "lightgray",
LEFT_BUTTON : "left_button_1", LEFT_BUTTON : "left_button_1",
RIGHT_BUTTON : "right_button_2" RIGHT_BUTTON : "right_button_2"
}; };

View File

@ -1,64 +1,64 @@
// TODO(grosbouddha): put under pskl namespace. // TODO(grosbouddha): put under pskl namespace.
Events = { Events = {
TOOL_SELECTED : "TOOL_SELECTED", TOOL_SELECTED : "TOOL_SELECTED",
TOOL_RELEASED : "TOOL_RELEASED", TOOL_RELEASED : "TOOL_RELEASED",
PRIMARY_COLOR_SELECTED: "PRIMARY_COLOR_SELECTED", PRIMARY_COLOR_SELECTED: "PRIMARY_COLOR_SELECTED",
PRIMARY_COLOR_UPDATED: "PRIMARY_COLOR_UPDATED", PRIMARY_COLOR_UPDATED: "PRIMARY_COLOR_UPDATED",
SECONDARY_COLOR_SELECTED: "SECONDARY_COLOR_SELECTED", SECONDARY_COLOR_SELECTED: "SECONDARY_COLOR_SELECTED",
SECONDARY_COLOR_UPDATED: "SECONDARY_COLOR_UPDATED", SECONDARY_COLOR_UPDATED: "SECONDARY_COLOR_UPDATED",
/** /**
* When this event is emitted, a request is sent to the localstorage * When this event is emitted, a request is sent to the localstorage
* Service to save the current framesheet. The storage service * Service to save the current framesheet. The storage service
* may not immediately store data (internal throttling of requests). * may not immediately store data (internal throttling of requests).
*/ */
LOCALSTORAGE_REQUEST: "LOCALSTORAGE_REQUEST", LOCALSTORAGE_REQUEST: "LOCALSTORAGE_REQUEST",
CANVAS_RIGHT_CLICKED: "CANVAS_RIGHT_CLICKED", CANVAS_RIGHT_CLICKED: "CANVAS_RIGHT_CLICKED",
/** /**
* Event to request a refresh of the display. * Event to request a refresh of the display.
* A bit overkill but, it's just workaround in our current drawing system. * A bit overkill but, it's just workaround in our current drawing system.
* TODO: Remove or rework when redraw system is refactored. * TODO: Remove or rework when redraw system is refactored.
*/ */
REFRESH: "REFRESH", REFRESH: "REFRESH",
/** /**
* Temporary event to bind the redraw of right preview film to the canvas. * Temporary event to bind the redraw of right preview film to the canvas.
* This redraw should be driven by model updates. * This redraw should be driven by model updates.
* TODO(vincz): Remove. * TODO(vincz): Remove.
*/ */
REDRAW_PREVIEWFILM: "REDRAW_PREVIEWFILM", REDRAW_PREVIEWFILM: "REDRAW_PREVIEWFILM",
/** /**
* Fired each time a user setting change. * Fired each time a user setting change.
* The payload will be: * The payload will be:
* 1st argument: Name of the settings * 1st argument: Name of the settings
* 2nd argument: New value * 2nd argument: New value
*/ */
USER_SETTINGS_CHANGED: "USER_SETTINGS_CHANGED", USER_SETTINGS_CHANGED: "USER_SETTINGS_CHANGED",
/** /**
* The framesheet was reseted and is now probably drastically different. * The framesheet was reseted and is now probably drastically different.
* Number of frames, content of frames, color used for the palette may have changed. * Number of frames, content of frames, color used for the palette may have changed.
*/ */
FRAMESHEET_RESET: "FRAMESHEET_RESET", 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_CREATED: "SELECTION_CREATED",
SELECTION_MOVE_REQUEST: "SELECTION_MOVE_REQUEST", SELECTION_MOVE_REQUEST: "SELECTION_MOVE_REQUEST",
SELECTION_DISMISSED: "SELECTION_DISMISSED", SELECTION_DISMISSED: "SELECTION_DISMISSED",
SHOW_NOTIFICATION: "SHOW_NOTIFICATION", SHOW_NOTIFICATION: "SHOW_NOTIFICATION",
HIDE_NOTIFICATION: "HIDE_NOTIFICATION", HIDE_NOTIFICATION: "HIDE_NOTIFICATION",
UNDO: "UNDO", UNDO: "UNDO",
REDO: "REDO", REDO: "REDO",
CUT: "CUT", CUT: "CUT",
COPY: "COPY", COPY: "COPY",
PASTE: "PASTE" PASTE: "PASTE"
}; };

View File

@ -1,67 +1,67 @@
(function () { (function () {
var ns = $.namespace("pskl.controller"); var ns = $.namespace("pskl.controller");
ns.AnimatedPreviewController = function (framesheet, container, dpi) { ns.AnimatedPreviewController = function (framesheet, container, dpi) {
this.framesheet = framesheet; this.framesheet = framesheet;
this.container = container; 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.currentIndex = 0;
this.elapsedTime = 0;
}
this.renderer.render(this.framesheet.getFrameByIndex(this.currentIndex));
}
};
this.fps = parseInt($("#preview-fps")[0].value, 10); /**
* Calculate the preview DPI depending on the framesheet size
var renderingOptions = { */
"dpi": this.calculateDPI_() ns.AnimatedPreviewController.prototype.calculateDPI_ = function () {
}; var previewSize = 200,
this.renderer = new pskl.rendering.FrameRenderer(this.container, renderingOptions); 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.updateDPI_ = function () {
}; this.dpi = this.calculateDPI_();
this.renderer.updateDPI(this.dpi);
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);
};
})(); })();

View File

@ -68,21 +68,21 @@
ns.DrawingController.prototype.initMouseBehavior = function() { ns.DrawingController.prototype.initMouseBehavior = function() {
var body = $('body'); var body = $('body');
this.container.mousedown($.proxy(this.onMousedown_, this)); this.container.mousedown($.proxy(this.onMousedown_, this));
this.container.mousemove($.proxy(this.onMousemove_, this)); this.container.mousemove($.proxy(this.onMousemove_, this));
body.mouseup($.proxy(this.onMouseup_, this)); body.mouseup($.proxy(this.onMouseup_, this));
// Deactivate right click: // Deactivate right click:
body.contextmenu(this.onCanvasContextMenu_); body.contextmenu(this.onCanvasContextMenu_);
}; };
ns.DrawingController.prototype.startDPIUpdateTimer_ = function () { ns.DrawingController.prototype.startDPIUpdateTimer_ = function () {
if (this.dpiUpdateTimer) { if (this.dpiUpdateTimer) {
window.clearInterval(this.dpiUpdateTimer); window.clearInterval(this.dpiUpdateTimer);
} }
this.dpiUpdateTimer = window.setTimeout($.proxy(this.updateDPI_, this), 200); this.dpiUpdateTimer = window.setTimeout($.proxy(this.updateDPI_, this), 200);
}, },
/** /**
@ -98,144 +98,144 @@
* @private * @private
*/ */
ns.DrawingController.prototype.onMousedown_ = function (event) { ns.DrawingController.prototype.onMousedown_ = function (event) {
this.isClicked = true; this.isClicked = true;
if(event.button == 2) { // right click if(event.button == 2) { // right click
this.isRightClicked = true; this.isRightClicked = true;
$.publish(Events.CANVAS_RIGHT_CLICKED); $.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( $.publish(Events.LOCALSTORAGE_REQUEST);
coords.col, coords.row, };
this.getCurrentColor_(),
this.framesheet.getCurrentFrame(),
this.overlayFrame,
this.wrapEvtInfo_(event)
);
$.publish(Events.LOCALSTORAGE_REQUEST);
};
/** /**
* @private * @private
*/ */
ns.DrawingController.prototype.onMousemove_ = function (event) { ns.DrawingController.prototype.onMousemove_ = function (event) {
var currentTime = new Date().getTime(); var currentTime = new Date().getTime();
// Throttling of the mousemove event: // Throttling of the mousemove event:
if ((currentTime - this.previousMousemoveTime) > 40 ) { if ((currentTime - this.previousMousemoveTime) > 40 ) {
var coords = this.getSpriteCoordinates(event); var coords = this.getSpriteCoordinates(event);
if (this.isClicked) { if (this.isClicked) {
this.currentToolBehavior.moveToolAt( 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(
coords.col, coords.row, coords.col, coords.row,
this.getCurrentColor_(), this.getCurrentColor_(),
this.framesheet.getCurrentFrame(), this.framesheet.getCurrentFrame(),
this.overlayFrame, this.overlayFrame,
this.wrapEvtInfo_(event) this.wrapEvtInfo_(event)
); );
$.publish(Events.TOOL_RELEASED); // 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);
/**
* @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 { } else {
return this.primaryColor;
}
};
/** this.currentToolBehavior.moveUnactiveToolAt(
* @private coords.col, coords.row,
*/ this.getCurrentColor_(),
ns.DrawingController.prototype.onCanvasContextMenu_ = function (event) { this.framesheet.getCurrentFrame(),
if ($(event.target).closest('#drawing-canvas-container').length) { this.overlayFrame,
// Deactivate right click on drawing canvas only. this.wrapEvtInfo_(event)
event.preventDefault(); );
event.stopPropagation(); }
event.cancelBubble = true; this.previousMousemoveTime = currentTime;
return false; }
} };
/**
* @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 () { ns.DrawingController.prototype.render = function () {
this.renderFrame(); this.renderFrame();
@ -271,11 +271,11 @@
*/ */
ns.DrawingController.prototype.calculateDPI_ = function() { ns.DrawingController.prototype.calculateDPI_ = function() {
var availableViewportHeight = $('#main-wrapper').height(), var availableViewportHeight = $('#main-wrapper').height(),
leftSectionWidth = $('.left-column').outerWidth(true), leftSectionWidth = $('.left-column').outerWidth(true),
rightSectionWidth = $('.right-column').outerWidth(true), rightSectionWidth = $('.right-column').outerWidth(true),
availableViewportWidth = $('#main-wrapper').width() - leftSectionWidth - rightSectionWidth, availableViewportWidth = $('#main-wrapper').width() - leftSectionWidth - rightSectionWidth,
framePixelHeight = this.framesheet.getCurrentFrame().getHeight(), framePixelHeight = this.framesheet.getCurrentFrame().getHeight(),
framePixelWidth = this.framesheet.getCurrentFrame().getWidth(); framePixelWidth = this.framesheet.getCurrentFrame().getWidth();
if (pskl.UserSettings.get(pskl.UserSettings.SHOW_GRID)) { if (pskl.UserSettings.get(pskl.UserSettings.SHOW_GRID)) {
availableViewportWidth = availableViewportWidth - (framePixelWidth * Constants.GRID_STROKE_WIDTH); availableViewportWidth = availableViewportWidth - (framePixelWidth * Constants.GRID_STROKE_WIDTH);
@ -299,7 +299,7 @@
var currentFrameHeight = this.framesheet.getCurrentFrame().getHeight(); var currentFrameHeight = this.framesheet.getCurrentFrame().getHeight();
var canvasHeight = currentFrameHeight * dpi; var canvasHeight = currentFrameHeight * dpi;
if (pskl.UserSettings.get(pskl.UserSettings.SHOW_GRID)) { 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); var verticalGapInPixel = Math.floor(($('#main-wrapper').height() - canvasHeight) / 2);

View File

@ -1,217 +1,217 @@
(function () { (function () {
var ns = $.namespace("pskl.controller"); var ns = $.namespace("pskl.controller");
ns.PreviewFilmController = function (framesheet, container, dpi) { ns.PreviewFilmController = function (framesheet, container, dpi) {
this.framesheet = framesheet; this.framesheet = framesheet;
this.container = container; this.container = container;
this.dpi = this.calculateDPI_(); this.dpi = this.calculateDPI_();
this.redrawFlag = true; this.redrawFlag = true;
$.subscribe(Events.TOOL_RELEASED, this.flagForRedraw_.bind(this)); $.subscribe(Events.TOOL_RELEASED, this.flagForRedraw_.bind(this));
$.subscribe(Events.FRAMESHEET_RESET, this.flagForRedraw_.bind(this)); $.subscribe(Events.FRAMESHEET_RESET, this.flagForRedraw_.bind(this));
$.subscribe(Events.FRAMESHEET_RESET, this.refreshDPI_.bind(this)); $.subscribe(Events.FRAMESHEET_RESET, this.refreshDPI_.bind(this));
$('#preview-list-scroller').scroll(this.updateScrollerOverflows.bind(this)); $('#preview-list-scroller').scroll(this.updateScrollerOverflows.bind(this));
this.updateScrollerOverflows(); this.updateScrollerOverflows();
}; };
ns.PreviewFilmController.prototype.init = function() {}; ns.PreviewFilmController.prototype.init = function() {};
ns.PreviewFilmController.prototype.addFrame = function () { ns.PreviewFilmController.prototype.addFrame = function () {
this.framesheet.addEmptyFrame(); this.framesheet.addEmptyFrame();
this.framesheet.setCurrentFrameIndex(this.framesheet.getFrameCount() - 1); this.framesheet.setCurrentFrameIndex(this.framesheet.getFrameCount() - 1);
this.updateScrollerOverflows(); this.updateScrollerOverflows();
}; };
ns.PreviewFilmController.prototype.flagForRedraw_ = function () { ns.PreviewFilmController.prototype.flagForRedraw_ = function () {
this.redrawFlag = true; this.redrawFlag = true;
}; };
ns.PreviewFilmController.prototype.refreshDPI_ = function () { ns.PreviewFilmController.prototype.refreshDPI_ = function () {
this.dpi = this.calculateDPI_(); this.dpi = this.calculateDPI_();
}; };
ns.PreviewFilmController.prototype.render = function () { ns.PreviewFilmController.prototype.render = function () {
if (this.redrawFlag) { if (this.redrawFlag) {
// TODO(vincz): Full redraw on any drawing modification, optimize. // TODO(vincz): Full redraw on any drawing modification, optimize.
this.createPreviews_(); this.createPreviews_();
this.redrawFlag = false; this.redrawFlag = false;
} }
}; };
ns.PreviewFilmController.prototype.updateScrollerOverflows = function () { ns.PreviewFilmController.prototype.updateScrollerOverflows = function () {
var scroller = $('#preview-list-scroller'); var scroller = $('#preview-list-scroller');
var scrollerHeight = scroller.height(); var scrollerHeight = scroller.height();
var scrollTop = scroller.scrollTop(); var scrollTop = scroller.scrollTop();
var scrollerContentHeight = $('#preview-list').height(); var scrollerContentHeight = $('#preview-list').height();
var treshold = $('.top-overflow').height(); var treshold = $('.top-overflow').height();
var overflowTop = false, var overflowTop = false,
overflowBottom = false; overflowBottom = false;
if (scrollerHeight < scrollerContentHeight) { if (scrollerHeight < scrollerContentHeight) {
if (scrollTop > treshold) { if (scrollTop > treshold) {
overflowTop = true; overflowTop = true;
} }
var scrollBottom = (scrollerContentHeight - scrollTop) - scrollerHeight; var scrollBottom = (scrollerContentHeight - scrollTop) - scrollerHeight;
if (scrollBottom > treshold) { if (scrollBottom > treshold) {
overflowBottom = true; overflowBottom = true;
} }
} }
var wrapper = $('#preview-list-wrapper'); var wrapper = $('#preview-list-wrapper');
wrapper.toggleClass('top-overflow-visible', overflowTop); wrapper.toggleClass('top-overflow-visible', overflowTop);
wrapper.toggleClass('bottom-overflow-visible', overflowBottom); wrapper.toggleClass('bottom-overflow-visible', overflowBottom);
}; };
ns.PreviewFilmController.prototype.createPreviews_ = function () { ns.PreviewFilmController.prototype.createPreviews_ = function () {
this.container.html(""); this.container.html("");
// Manually remove tooltips since mouseout events were shortcut by the DOM refresh: // Manually remove tooltips since mouseout events were shortcut by the DOM refresh:
$(".tooltip").remove(); $(".tooltip").remove();
var frameCount = this.framesheet.getFrameCount(); var frameCount = this.framesheet.getFrameCount();
for (var i = 0, l = frameCount; i < l ; i++) { for (var i = 0, l = frameCount; i < l ; i++) {
this.container.append(this.createPreviewTile_(i)); this.container.append(this.createPreviewTile_(i));
} }
// Append 'new empty frame' button // Append 'new empty frame' button
var newFrameButton = document.createElement("div"); var newFrameButton = document.createElement("div");
newFrameButton.id = "add-frame-action"; newFrameButton.id = "add-frame-action";
newFrameButton.className = "add-frame-action"; newFrameButton.className = "add-frame-action";
newFrameButton.innerHTML = "<p class='label'>Add new frame</p>"; newFrameButton.innerHTML = "<p class='label'>Add new frame</p>";
this.container.append(newFrameButton); this.container.append(newFrameButton);
$(newFrameButton).click(this.addFrame.bind(this)); $(newFrameButton).click(this.addFrame.bind(this));
var needDragndropBehavior = (frameCount > 1); var needDragndropBehavior = (frameCount > 1);
if(needDragndropBehavior) { if(needDragndropBehavior) {
this.initDragndropBehavior_(); this.initDragndropBehavior_();
} }
this.updateScrollerOverflows(); this.updateScrollerOverflows();
}; };
/** /**
* @private * @private
*/ */
ns.PreviewFilmController.prototype.initDragndropBehavior_ = function () { ns.PreviewFilmController.prototype.initDragndropBehavior_ = function () {
$("#preview-list").sortable({ $("#preview-list").sortable({
placeholder: "preview-tile-drop-proxy", placeholder: "preview-tile-drop-proxy",
update: $.proxy(this.onUpdate_, this), update: $.proxy(this.onUpdate_, this),
items: ".preview-tile" items: ".preview-tile"
}); });
$("#preview-list").disableSelection(); $("#preview-list").disableSelection();
}; };
/** /**
* @private * @private
*/ */
ns.PreviewFilmController.prototype.onUpdate_ = function( event, ui ) { ns.PreviewFilmController.prototype.onUpdate_ = function( event, ui ) {
var originFrameId = parseInt(ui.item.data("tile-number"), 10); var originFrameId = parseInt(ui.item.data("tile-number"), 10);
var targetInsertionId = $('.preview-tile').index(ui.item); var targetInsertionId = $('.preview-tile').index(ui.item);
this.framesheet.moveFrame(originFrameId, targetInsertionId); this.framesheet.moveFrame(originFrameId, targetInsertionId);
this.framesheet.setCurrentFrameIndex(targetInsertionId); this.framesheet.setCurrentFrameIndex(targetInsertionId);
// TODO(grosbouddha): move localstorage request to the model layer? // TODO(grosbouddha): move localstorage request to the model layer?
$.publish(Events.LOCALSTORAGE_REQUEST); $.publish(Events.LOCALSTORAGE_REQUEST);
}; };
/** /**
* @private * @private
* TODO(vincz): clean this giant rendering function & remove listeners. * TODO(vincz): clean this giant rendering function & remove listeners.
*/ */
ns.PreviewFilmController.prototype.createPreviewTile_ = function(tileNumber) { ns.PreviewFilmController.prototype.createPreviewTile_ = function(tileNumber) {
var currentFrame = this.framesheet.getFrameByIndex(tileNumber); var currentFrame = this.framesheet.getFrameByIndex(tileNumber);
var previewTileRoot = document.createElement("li"); var previewTileRoot = document.createElement("li");
var classname = "preview-tile"; var classname = "preview-tile";
previewTileRoot.setAttribute("data-tile-number", tileNumber); previewTileRoot.setAttribute("data-tile-number", tileNumber);
if (this.framesheet.getCurrentFrame() == currentFrame) { if (this.framesheet.getCurrentFrame() == currentFrame) {
classname += " selected"; classname += " selected";
} }
previewTileRoot.className = classname; previewTileRoot.className = classname;
var canvasContainer = document.createElement("div"); var canvasContainer = document.createElement("div");
canvasContainer.className = "canvas-container"; canvasContainer.className = "canvas-container";
var canvasBackground = document.createElement("div"); var canvasBackground = document.createElement("div");
canvasBackground.className = "canvas-background"; canvasBackground.className = "canvas-background";
canvasContainer.appendChild(canvasBackground); canvasContainer.appendChild(canvasBackground);
previewTileRoot.addEventListener('click', this.onPreviewClick_.bind(this, tileNumber)); previewTileRoot.addEventListener('click', this.onPreviewClick_.bind(this, tileNumber));
var cloneFrameButton = document.createElement("button"); var cloneFrameButton = document.createElement("button");
cloneFrameButton.setAttribute('rel', 'tooltip'); cloneFrameButton.setAttribute('rel', 'tooltip');
cloneFrameButton.setAttribute('data-placement', 'right'); cloneFrameButton.setAttribute('data-placement', 'right');
cloneFrameButton.setAttribute('title', 'Duplicate this frame'); cloneFrameButton.setAttribute('title', 'Duplicate this frame');
cloneFrameButton.className = "tile-overlay duplicate-frame-action"; cloneFrameButton.className = "tile-overlay duplicate-frame-action";
previewTileRoot.appendChild(cloneFrameButton); previewTileRoot.appendChild(cloneFrameButton);
cloneFrameButton.addEventListener('click', this.onAddButtonClick_.bind(this, tileNumber)); cloneFrameButton.addEventListener('click', this.onAddButtonClick_.bind(this, tileNumber));
// TODO(vincz): Eventually optimize this part by not recreating a FrameRenderer. Note that the real optim // 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. // is to make this update function (#createPreviewTile) less aggressive.
var renderingOptions = {"dpi": this.dpi }; var renderingOptions = {"dpi": this.dpi };
var currentFrameRenderer = new pskl.rendering.FrameRenderer($(canvasContainer), renderingOptions, "tile-view"); var currentFrameRenderer = new pskl.rendering.FrameRenderer($(canvasContainer), renderingOptions, "tile-view");
currentFrameRenderer.init(currentFrame); currentFrameRenderer.init(currentFrame);
previewTileRoot.appendChild(canvasContainer); previewTileRoot.appendChild(canvasContainer);
if(tileNumber > 0 || this.framesheet.getFrameCount() > 1) { if(tileNumber > 0 || this.framesheet.getFrameCount() > 1) {
// Add 'remove frame' button. // Add 'remove frame' button.
var deleteButton = document.createElement("button"); var deleteButton = document.createElement("button");
deleteButton.setAttribute('rel', 'tooltip'); deleteButton.setAttribute('rel', 'tooltip');
deleteButton.setAttribute('data-placement', 'right'); deleteButton.setAttribute('data-placement', 'right');
deleteButton.setAttribute('title', 'Delete this frame'); deleteButton.setAttribute('title', 'Delete this frame');
deleteButton.className = "tile-overlay delete-frame-action"; deleteButton.className = "tile-overlay delete-frame-action";
deleteButton.addEventListener('click', this.onDeleteButtonClick_.bind(this, tileNumber)); deleteButton.addEventListener('click', this.onDeleteButtonClick_.bind(this, tileNumber));
previewTileRoot.appendChild(deleteButton); previewTileRoot.appendChild(deleteButton);
// Add 'dragndrop handle'. // Add 'dragndrop handle'.
var dndHandle = document.createElement("div"); var dndHandle = document.createElement("div");
dndHandle.className = "tile-overlay dnd-action"; dndHandle.className = "tile-overlay dnd-action";
previewTileRoot.appendChild(dndHandle); previewTileRoot.appendChild(dndHandle);
} }
var tileCount = document.createElement("div"); var tileCount = document.createElement("div");
tileCount.className = "tile-overlay tile-count"; tileCount.className = "tile-overlay tile-count";
tileCount.innerHTML = tileNumber; tileCount.innerHTML = tileNumber;
previewTileRoot.appendChild(tileCount); previewTileRoot.appendChild(tileCount);
return previewTileRoot; return previewTileRoot;
}; };
ns.PreviewFilmController.prototype.onPreviewClick_ = function (index, evt) { ns.PreviewFilmController.prototype.onPreviewClick_ = function (index, evt) {
// has not class tile-action: // has not class tile-action:
if(!evt.target.classList.contains('tile-overlay')) { if(!evt.target.classList.contains('tile-overlay')) {
this.framesheet.setCurrentFrameIndex(index); this.framesheet.setCurrentFrameIndex(index);
} }
}; };
ns.PreviewFilmController.prototype.onDeleteButtonClick_ = function (index, evt) { ns.PreviewFilmController.prototype.onDeleteButtonClick_ = function (index, evt) {
this.framesheet.removeFrameByIndex(index); this.framesheet.removeFrameByIndex(index);
$.publish(Events.LOCALSTORAGE_REQUEST); // Should come from model $.publish(Events.LOCALSTORAGE_REQUEST); // Should come from model
this.updateScrollerOverflows(); this.updateScrollerOverflows();
}; };
ns.PreviewFilmController.prototype.onAddButtonClick_ = function (index, evt) { ns.PreviewFilmController.prototype.onAddButtonClick_ = function (index, evt) {
this.framesheet.duplicateFrameByIndex(index); this.framesheet.duplicateFrameByIndex(index);
$.publish(Events.LOCALSTORAGE_REQUEST); // Should come from model $.publish(Events.LOCALSTORAGE_REQUEST); // Should come from model
this.framesheet.setCurrentFrameIndex(index + 1); this.framesheet.setCurrentFrameIndex(index + 1);
this.updateScrollerOverflows(); this.updateScrollerOverflows();
}; };
/** /**
* Calculate the preview DPI depending on the framesheet size * Calculate the preview DPI depending on the framesheet size
*/ */
ns.PreviewFilmController.prototype.calculateDPI_ = function () { ns.PreviewFilmController.prototype.calculateDPI_ = function () {
var previewSize = 120, var previewSize = 120,
framePixelHeight = this.framesheet.getCurrentFrame().getHeight(), framePixelHeight = this.framesheet.getCurrentFrame().getHeight(),
framePixelWidth = this.framesheet.getCurrentFrame().getWidth(); framePixelWidth = this.framesheet.getCurrentFrame().getWidth();
// TODO (julz) : should have a utility to get a Size from framesheet easily (what about empty framesheets though ?) // TODO (julz) : should have a utility to get a Size from framesheet easily (what about empty framesheets though ?)
return pskl.PixelUtils.calculateDPI(previewSize, previewSize, framePixelHeight, framePixelWidth); return pskl.PixelUtils.calculateDPI(previewSize, previewSize, framePixelHeight, framePixelWidth);
}; };
})(); })();

View File

@ -1,45 +1,45 @@
(function () { (function () {
var ns = $.namespace("pskl.controller"); var ns = $.namespace("pskl.controller");
ns.SettingsController = function () {}; ns.SettingsController = function () {};
/** /**
* @public * @public
*/ */
ns.SettingsController.prototype.init = function() { ns.SettingsController.prototype.init = function() {
// Highlight selected background picker: // Highlight selected background picker:
var backgroundClass = pskl.UserSettings.get(pskl.UserSettings.CANVAS_BACKGROUND); var backgroundClass = pskl.UserSettings.get(pskl.UserSettings.CANVAS_BACKGROUND);
$('#background-picker-wrapper') $('#background-picker-wrapper')
.find('.background-picker[data-background-class=' + backgroundClass + ']') .find('.background-picker[data-background-class=' + backgroundClass + ']')
.addClass('selected'); .addClass('selected');
// Initial state for grid display: // Initial state for grid display:
var show_grid = pskl.UserSettings.get(pskl.UserSettings.SHOW_GRID); var show_grid = pskl.UserSettings.get(pskl.UserSettings.SHOW_GRID);
$('#show-grid').prop('checked', show_grid); $('#show-grid').prop('checked', show_grid);
// Expand drawer when clicking 'Settings' tab. // Expand drawer when clicking 'Settings' tab.
$('#settings').click(function(evt) { $('#settings').click(function(evt) {
$('.right-sticky-section').toggleClass('expanded'); $('.right-sticky-section').toggleClass('expanded');
$('#settings').toggleClass('has-expanded-drawer'); $('#settings').toggleClass('has-expanded-drawer');
}); });
// Handle grid display changes: // Handle grid display changes:
$('#show-grid').change($.proxy(function(evt) { $('#show-grid').change($.proxy(function(evt) {
var checked = $('#show-grid').prop('checked'); var checked = $('#show-grid').prop('checked');
pskl.UserSettings.set(pskl.UserSettings.SHOW_GRID, checked); pskl.UserSettings.set(pskl.UserSettings.SHOW_GRID, checked);
}, this)); }, this));
// Handle canvas background changes: // Handle canvas background changes:
$('#background-picker-wrapper').click(function(evt) { $('#background-picker-wrapper').click(function(evt) {
var target = $(evt.target).closest('.background-picker'); var target = $(evt.target).closest('.background-picker');
if (target.length) { if (target.length) {
var backgroundClass = target.data('background-class'); var backgroundClass = target.data('background-class');
pskl.UserSettings.set(pskl.UserSettings.CANVAS_BACKGROUND, backgroundClass); pskl.UserSettings.set(pskl.UserSettings.CANVAS_BACKGROUND, backgroundClass);
$('.background-picker').removeClass('selected'); $('.background-picker').removeClass('selected');
target.addClass('selected'); target.addClass('selected');
} }
}); });
}; };
})(); })();

View File

@ -1,99 +1,99 @@
(function () { (function () {
var ns = $.namespace("pskl.controller"); var ns = $.namespace("pskl.controller");
ns.ToolController = function () { ns.ToolController = function () {
this.toolInstances = { this.toolInstances = {
"simplePen" : new pskl.drawingtools.SimplePen(), "simplePen" : new pskl.drawingtools.SimplePen(),
"verticalMirrorPen" : new pskl.drawingtools.VerticalMirrorPen(), "verticalMirrorPen" : new pskl.drawingtools.VerticalMirrorPen(),
"eraser" : new pskl.drawingtools.Eraser(), "eraser" : new pskl.drawingtools.Eraser(),
"paintBucket" : new pskl.drawingtools.PaintBucket(), "paintBucket" : new pskl.drawingtools.PaintBucket(),
"stroke" : new pskl.drawingtools.Stroke(), "stroke" : new pskl.drawingtools.Stroke(),
"rectangle" : new pskl.drawingtools.Rectangle(), "rectangle" : new pskl.drawingtools.Rectangle(),
"circle" : new pskl.drawingtools.Circle(), "circle" : new pskl.drawingtools.Circle(),
"move" : new pskl.drawingtools.Move(), "move" : new pskl.drawingtools.Move(),
"rectangleSelect" : new pskl.drawingtools.RectangleSelect(), "rectangleSelect" : new pskl.drawingtools.RectangleSelect(),
"shapeSelect" : new pskl.drawingtools.ShapeSelect(), "shapeSelect" : new pskl.drawingtools.ShapeSelect(),
"colorPicker" : new pskl.drawingtools.ColorPicker() "colorPicker" : new pskl.drawingtools.ColorPicker()
};
this.currentSelectedTool = this.toolInstances.simplePen;
this.previousSelectedTool = this.toolInstances.simplePen;
}; };
/** this.currentSelectedTool = this.toolInstances.simplePen;
* @private this.previousSelectedTool = this.toolInstances.simplePen;
*/ };
ns.ToolController.prototype.activateToolOnStage_ = function(tool) {
var stage = $("body"); /**
var previousSelectedToolClass = stage.data("selected-tool-class"); * @private
if(previousSelectedToolClass) { */
stage.removeClass(previousSelectedToolClass); 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);
};
/**
* @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.onToolIconClicked_ = function(evt) {
var target = $(evt.target);
var clickedTool = target.closest(".tool-icon");
if(clickedTool.length) {
for(var tool in this.toolInstances) {
if (this.toolInstances[tool].toolId == clickedTool.data().toolId) {
this.selectTool_(this.toolInstances[tool]);
// Show tool as selected:
$('#tool-section .tool-icon.selected').removeClass('selected');
clickedTool.addClass('selected');
} }
stage.addClass(tool.toolId); }
stage.data("selected-tool-class", tool.toolId); }
}; };
/** /**
* @private * @private
*/ */
ns.ToolController.prototype.selectTool_ = function(tool) { ns.ToolController.prototype.createToolMarkup_ = function() {
console.log("Selecting Tool:" , this.currentSelectedTool); var currentTool, toolMarkup = '', extraClass;
this.currentSelectedTool = tool; // TODO(vincz): Tools rendering order is not enforced by the data stucture (this.toolInstances), fix that.
this.activateToolOnStage_(this.currentSelectedTool); for (var toolKey in this.toolInstances) {
$.publish(Events.TOOL_SELECTED, [tool]); currentTool = this.toolInstances[toolKey];
}; extraClass = currentTool.toolId;
if (this.currentSelectedTool == currentTool) {
extraClass = extraClass + " selected";
}
toolMarkup += '<li rel="tooltip" data-placement="right" class="tool-icon ' + extraClass + '" data-tool-id="' + currentTool.toolId +
'" title="' + currentTool.helpText + '"></li>';
}
$('#tools-container').html(toolMarkup);
};
/** /**
* @private * @public
*/ */
ns.ToolController.prototype.onToolIconClicked_ = function(evt) { ns.ToolController.prototype.init = function() {
var target = $(evt.target);
var clickedTool = target.closest(".tool-icon");
if(clickedTool.length) { this.createToolMarkup_();
for(var tool in this.toolInstances) {
if (this.toolInstances[tool].toolId == clickedTool.data().toolId) {
this.selectTool_(this.toolInstances[tool]);
// Show tool as selected: // Initialize tool:
$('#tool-section .tool-icon.selected').removeClass('selected'); // Set SimplePen as default selected tool:
clickedTool.addClass('selected'); this.selectTool_(this.toolInstances.simplePen);
} // Activate listener on tool panel:
} $("#tool-section").click($.proxy(this.onToolIconClicked_, this));
} };
};
/**
* @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 += '<li rel="tooltip" data-placement="right" class="tool-icon ' + extraClass + '" data-tool-id="' + currentTool.toolId +
'" title="' + currentTool.helpText + '"></li>';
}
$('#tools-container').html(toolMarkup);
};
/**
* @public
*/
ns.ToolController.prototype.init = function() {
this.createToolMarkup_();
// 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));
};
})(); })();

View File

@ -4,69 +4,69 @@
* @require pskl.utils * @require pskl.utils
*/ */
(function() { (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.applyToolAt = function(col, row, color, frame, overlay) {};
ns.BaseTool.prototype.moveToolAt = 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) { ns.BaseTool.prototype.moveUnactiveToolAt = function(col, row, color, frame, overlay) {
if (overlay.containsPixel(col, row)) { if (overlay.containsPixel(col, row)) {
if (!isNaN(this.highlightedPixelCol) && if (!isNaN(this.highlightedPixelCol) &&
!isNaN(this.highlightedPixelRow) && !isNaN(this.highlightedPixelRow) &&
(this.highlightedPixelRow != row || (this.highlightedPixelRow != row ||
this.highlightedPixelCol != col)) { this.highlightedPixelCol != col)) {
// Clean the previously highlighted pixel: // Clean the previously highlighted pixel:
overlay.clear(); overlay.clear();
} }
// Show the current pixel targeted by the tool: // Show the current pixel targeted by the tool:
overlay.setPixel(col, row, Constants.TOOL_TARGET_HIGHLIGHT_COLOR); overlay.setPixel(col, row, Constants.TOOL_TARGET_HIGHLIGHT_COLOR);
this.highlightedPixelCol = col; this.highlightedPixelCol = col;
this.highlightedPixelRow = row; 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 * Bresenham line algorihtm: Get an array of pixels from
* start and end coordinates. * start and end coordinates.
* *
* http://en.wikipedia.org/wiki/Bresenham's_line_algorithm * http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
* http://stackoverflow.com/questions/4672279/bresenham-algorithm-in-javascript * http://stackoverflow.com/questions/4672279/bresenham-algorithm-in-javascript
* *
* @private * @private
*/ */
ns.BaseTool.prototype.getLinePixels_ = function(x0, x1, y0, y1) { ns.BaseTool.prototype.getLinePixels_ = function(x0, x1, y0, y1) {
var pixels = []; var pixels = [];
var dx = Math.abs(x1-x0); var dx = Math.abs(x1-x0);
var dy = Math.abs(y1-y0); var dy = Math.abs(y1-y0);
var sx = (x0 < x1) ? 1 : -1; var sx = (x0 < x1) ? 1 : -1;
var sy = (y0 < y1) ? 1 : -1; var sy = (y0 < y1) ? 1 : -1;
var err = dx-dy; var err = dx-dy;
while(true){ while(true){
// Do what you need to for this // Do what you need to for this
pixels.push({"col": x0, "row": y0}); pixels.push({"col": x0, "row": y0});
if ((x0==x1) && (y0==y1)) break; if ((x0==x1) && (y0==y1)) break;
var e2 = 2*err; var e2 = 2*err;
if (e2>-dy){ if (e2>-dy){
err -= dy; err -= dy;
x0 += sx; x0 += sx;
} }
if (e2 < dx) { if (e2 < dx) {
err += dx; err += dx;
y0 += sy; y0 += sy;
} }
} }
return pixels; return pixels;
}; };
})(); })();

View File

@ -4,82 +4,82 @@
* @require pskl.utils * @require pskl.utils
*/ */
(function() { (function() {
var ns = $.namespace("pskl.drawingtools"); var ns = $.namespace("pskl.drawingtools");
ns.Circle = function() { ns.Circle = function() {
this.toolId = "tool-circle"; this.toolId = "tool-circle";
this.helpText = "Circle tool"; this.helpText = "Circle tool";
// Circle's first point coordinates (set in applyToolAt) // Circle's first point coordinates (set in applyToolAt)
this.startCol = null; this.startCol = null;
this.startRow = null; this.startRow = null;
}; };
pskl.utils.inherit(ns.Circle, ns.BaseTool); pskl.utils.inherit(ns.Circle, ns.BaseTool);
/** /**
* @override * @override
*/ */
ns.Circle.prototype.applyToolAt = function(col, row, color, frame, overlay) { ns.Circle.prototype.applyToolAt = function(col, row, color, frame, overlay) {
this.startCol = col; this.startCol = col;
this.startRow = row; this.startRow = row;
// Drawing the first point of the rectangle in the fake overlay canvas: // Drawing the first point of the rectangle in the fake overlay canvas:
overlay.setPixel(col, row, color); overlay.setPixel(col, row, color);
}; };
ns.Circle.prototype.moveToolAt = function(col, row, color, frame, overlay) { ns.Circle.prototype.moveToolAt = function(col, row, color, frame, overlay) {
overlay.clear(); overlay.clear();
if(color == Constants.TRANSPARENT_COLOR) { if(color == Constants.TRANSPARENT_COLOR) {
color = Constants.SELECTION_TRANSPARENT_COLOR; color = Constants.SELECTION_TRANSPARENT_COLOR;
} }
// draw in overlay // draw in overlay
this.drawCircle_(col, row, color, overlay); this.drawCircle_(col, row, color, overlay);
}; };
/** /**
* @override * @override
*/ */
ns.Circle.prototype.releaseToolAt = function(col, row, color, frame, overlay) { ns.Circle.prototype.releaseToolAt = function(col, row, color, frame, overlay) {
overlay.clear(); overlay.clear();
if(frame.containsPixel(col, row)) { // cancel if outside of canvas if(frame.containsPixel(col, row)) { // cancel if outside of canvas
// draw in frame to finalize // draw in frame to finalize
this.drawCircle_(col, row, color, frame); this.drawCircle_(col, row, color, frame);
} }
}; };
ns.Circle.prototype.drawCircle_ = function (col, row, color, targetFrame) { ns.Circle.prototype.drawCircle_ = function (col, row, color, targetFrame) {
var circlePoints = this.getCirclePixels_(this.startCol, this.startRow, col, row); var circlePoints = this.getCirclePixels_(this.startCol, this.startRow, col, row);
for(var i = 0; i< circlePoints.length; i++) { for(var i = 0; i< circlePoints.length; i++) {
// Change model: // Change model:
targetFrame.setPixel(circlePoints[i].col, circlePoints[i].row, color); targetFrame.setPixel(circlePoints[i].col, circlePoints[i].row, color);
} }
}; };
ns.Circle.prototype.getCirclePixels_ = function (x0, y0, x1, y1) { ns.Circle.prototype.getCirclePixels_ = function (x0, y0, x1, y1) {
var coords = pskl.PixelUtils.getOrderedRectangleCoordinates(x0, y0, x1, y1); var coords = pskl.PixelUtils.getOrderedRectangleCoordinates(x0, y0, x1, y1);
var xC = (coords.x0 + coords.x1)/2; var xC = (coords.x0 + coords.x1)/2;
var yC = (coords.y0 + coords.y1)/2; var yC = (coords.y0 + coords.y1)/2;
var rX = coords.x1 - xC; var rX = coords.x1 - xC;
var rY = coords.y1 - yC; var rY = coords.y1 - yC;
var pixels = []; var pixels = [];
var x, y, angle; var x, y, angle;
for (x = coords.x0 ; x < coords.x1 ; x++) { for (x = coords.x0 ; x < coords.x1 ; x++) {
angle = Math.acos((x - xC)/rX); angle = Math.acos((x - xC)/rX);
y = Math.round(rY * Math.sin(angle) + yC); y = Math.round(rY * Math.sin(angle) + yC);
pixels.push({"col": x, "row": y}); pixels.push({"col": x, "row": y});
pixels.push({"col": 2*xC - x, "row": 2*yC - y}); pixels.push({"col": 2*xC - x, "row": 2*yC - y});
} }
for (y = coords.y0 ; y < coords.y1 ; y++) { for (y = coords.y0 ; y < coords.y1 ; y++) {
angle = Math.asin((y - yC)/rY); angle = Math.asin((y - yC)/rY);
x = Math.round(rX * Math.cos(angle) + xC); x = Math.round(rX * Math.cos(angle) + xC);
pixels.push({"col": x, "row": y}); pixels.push({"col": x, "row": y});
pixels.push({"col": 2*xC - x, "row": 2*yC - y}); pixels.push({"col": 2*xC - x, "row": 2*yC - y});
} }
return pixels; return pixels;
}; };
})(); })();

View File

@ -4,26 +4,26 @@
* @require pskl.utils * @require pskl.utils
*/ */
(function() { (function() {
var ns = $.namespace("pskl.drawingtools"); var ns = $.namespace("pskl.drawingtools");
ns.ColorPicker = function() { ns.ColorPicker = function() {
this.toolId = "tool-colorpicker"; this.toolId = "tool-colorpicker";
this.helpText = "Color picker"; this.helpText = "Color picker";
}; };
pskl.utils.inherit(ns.ColorPicker, ns.BaseTool); pskl.utils.inherit(ns.ColorPicker, ns.BaseTool);
/** /**
* @override * @override
*/ */
ns.ColorPicker.prototype.applyToolAt = function(col, row, color, frame, overlay, context) { ns.ColorPicker.prototype.applyToolAt = function(col, row, color, frame, overlay, context) {
if (frame.containsPixel(col, row)) { if (frame.containsPixel(col, row)) {
var sampledColor = frame.getPixel(col, row); var sampledColor = frame.getPixel(col, row);
if (context.button == Constants.LEFT_BUTTON) { if (context.button == Constants.LEFT_BUTTON) {
$.publish(Events.PRIMARY_COLOR_SELECTED, [sampledColor]); $.publish(Events.PRIMARY_COLOR_SELECTED, [sampledColor]);
} else if (context.button == Constants.RIGHT_BUTTON) { } else if (context.button == Constants.RIGHT_BUTTON) {
$.publish(Events.SECONDARY_COLOR_SELECTED, [sampledColor]); $.publish(Events.SECONDARY_COLOR_SELECTED, [sampledColor]);
} }
} }
}; };
})(); })();

View File

@ -4,20 +4,20 @@
* @require Constants * @require Constants
* @require pskl.utils * @require pskl.utils
*/ */
(function() { (function() {
var ns = $.namespace("pskl.drawingtools"); var ns = $.namespace("pskl.drawingtools");
ns.Eraser = function() { ns.Eraser = function() {
this.toolId = "tool-eraser"; this.toolId = "tool-eraser";
this.helpText = "Eraser tool"; this.helpText = "Eraser tool";
}; };
pskl.utils.inherit(ns.Eraser, ns.SimplePen); pskl.utils.inherit(ns.Eraser, ns.SimplePen);
/** /**
* @override * @override
*/ */
ns.Eraser.prototype.applyToolAt = function(col, row, color, frame, overlay) { ns.Eraser.prototype.applyToolAt = function(col, row, color, frame, overlay) {
this.superclass.applyToolAt.call(this, col, row, Constants.TRANSPARENT_COLOR, frame, overlay); this.superclass.applyToolAt.call(this, col, row, Constants.TRANSPARENT_COLOR, frame, overlay);
}; };
})(); })();

View File

@ -4,51 +4,51 @@
* @require pskl.utils * @require pskl.utils
*/ */
(function() { (function() {
var ns = $.namespace("pskl.drawingtools"); var ns = $.namespace("pskl.drawingtools");
ns.Move = function() { ns.Move = function() {
this.toolId = "tool-move"; this.toolId = "tool-move";
this.helpText = "Move tool"; this.helpText = "Move tool";
// Stroke's first point coordinates (set in applyToolAt) // Stroke's first point coordinates (set in applyToolAt)
this.startCol = null; this.startCol = null;
this.startRow = null; this.startRow = null;
}; };
pskl.utils.inherit(ns.Move, ns.BaseTool); pskl.utils.inherit(ns.Move, ns.BaseTool);
/** /**
* @override * @override
*/ */
ns.Move.prototype.applyToolAt = function(col, row, color, frame, overlay) { ns.Move.prototype.applyToolAt = function(col, row, color, frame, overlay) {
this.startCol = col; this.startCol = col;
this.startRow = row; this.startRow = row;
this.frameClone = frame.clone(); this.frameClone = frame.clone();
}; };
ns.Move.prototype.moveToolAt = function(col, row, color, frame, overlay) { ns.Move.prototype.moveToolAt = function(col, row, color, frame, overlay) {
var colDiff = col - this.startCol, rowDiff = row - this.startRow; var colDiff = col - this.startCol, rowDiff = row - this.startRow;
this.shiftFrame(colDiff, rowDiff, frame, this.frameClone); this.shiftFrame(colDiff, rowDiff, frame, this.frameClone);
}; };
ns.Move.prototype.shiftFrame = function (colDiff, rowDiff, frame, reference) { ns.Move.prototype.shiftFrame = function (colDiff, rowDiff, frame, reference) {
var color; var color;
for (var col = 0 ; col < frame.getWidth() ; col++) { for (var col = 0 ; col < frame.getWidth() ; col++) {
for (var row = 0 ; row < frame.getHeight() ; row++) { for (var row = 0 ; row < frame.getHeight() ; row++) {
if (reference.containsPixel(col - colDiff, row - rowDiff)) { if (reference.containsPixel(col - colDiff, row - rowDiff)) {
color = reference.getPixel(col - colDiff, row - rowDiff); color = reference.getPixel(col - colDiff, row - rowDiff);
} else { } else {
color = Constants.TRANSPARENT_COLOR; color = Constants.TRANSPARENT_COLOR;
} }
frame.setPixel(col, row, color); frame.setPixel(col, row, color);
} }
} }
}; };
/** /**
* @override * @override
*/ */
ns.Move.prototype.releaseToolAt = function(col, row, color, frame, overlay) { ns.Move.prototype.releaseToolAt = function(col, row, color, frame, overlay) {
this.moveToolAt(col, row, color, frame, overlay); this.moveToolAt(col, row, color, frame, overlay);
}; };
})(); })();

View File

@ -4,22 +4,22 @@
* @require pskl.utils * @require pskl.utils
*/ */
(function() { (function() {
var ns = $.namespace("pskl.drawingtools"); var ns = $.namespace("pskl.drawingtools");
ns.PaintBucket = function() { ns.PaintBucket = function() {
this.toolId = "tool-paint-bucket"; this.toolId = "tool-paint-bucket";
this.helpText = "Paint bucket tool"; this.helpText = "Paint bucket tool";
}; };
pskl.utils.inherit(ns.PaintBucket, ns.BaseTool); pskl.utils.inherit(ns.PaintBucket, ns.BaseTool);
/** /**
* @override * @override
*/ */
ns.PaintBucket.prototype.applyToolAt = function(col, row, color, frame, overlay) { ns.PaintBucket.prototype.applyToolAt = function(col, row, color, frame, overlay) {
pskl.PixelUtils.paintSimilarConnectedPixelsFromFrame(frame, col, row, color); pskl.PixelUtils.paintSimilarConnectedPixelsFromFrame(frame, col, row, color);
}; };
})(); })();

View File

@ -4,56 +4,56 @@
* @require pskl.utils * @require pskl.utils
*/ */
(function() { (function() {
var ns = $.namespace("pskl.drawingtools"); var ns = $.namespace("pskl.drawingtools");
ns.Rectangle = function() { ns.Rectangle = function() {
this.toolId = "tool-rectangle"; this.toolId = "tool-rectangle";
this.helpText = "Rectangle tool"; this.helpText = "Rectangle tool";
// Rectangle's first point coordinates (set in applyToolAt) // Rectangle's first point coordinates (set in applyToolAt)
this.startCol = null; this.startCol = null;
this.startRow = null; this.startRow = null;
}; };
pskl.utils.inherit(ns.Rectangle, ns.BaseTool); pskl.utils.inherit(ns.Rectangle, ns.BaseTool);
/** /**
* @override * @override
*/ */
ns.Rectangle.prototype.applyToolAt = function(col, row, color, frame, overlay) { ns.Rectangle.prototype.applyToolAt = function(col, row, color, frame, overlay) {
this.startCol = col; this.startCol = col;
this.startRow = row; this.startRow = row;
// Drawing the first point of the rectangle in the fake overlay canvas: // Drawing the first point of the rectangle in the fake overlay canvas:
overlay.setPixel(col, row, color); overlay.setPixel(col, row, color);
}; };
ns.Rectangle.prototype.moveToolAt = function(col, row, color, frame, overlay) { ns.Rectangle.prototype.moveToolAt = function(col, row, color, frame, overlay) {
overlay.clear(); overlay.clear();
if(color == Constants.TRANSPARENT_COLOR) { if(color == Constants.TRANSPARENT_COLOR) {
color = Constants.SELECTION_TRANSPARENT_COLOR; color = Constants.SELECTION_TRANSPARENT_COLOR;
} }
// draw in overlay // draw in overlay
this.drawRectangle_(col, row, color, overlay); this.drawRectangle_(col, row, color, overlay);
}; };
/** /**
* @override * @override
*/ */
ns.Rectangle.prototype.releaseToolAt = function(col, row, color, frame, overlay) { ns.Rectangle.prototype.releaseToolAt = function(col, row, color, frame, overlay) {
overlay.clear(); overlay.clear();
if(frame.containsPixel(col, row)) { // cancel if outside of canvas if(frame.containsPixel(col, row)) { // cancel if outside of canvas
// draw in frame to finalize // draw in frame to finalize
this.drawRectangle_(col, row, color, frame); this.drawRectangle_(col, row, color, frame);
} }
}; };
ns.Rectangle.prototype.drawRectangle_ = function (col, row, color, targetFrame) { ns.Rectangle.prototype.drawRectangle_ = function (col, row, color, targetFrame) {
var strokePoints = pskl.PixelUtils.getBoundRectanglePixels(this.startCol, this.startRow, col, row); var strokePoints = pskl.PixelUtils.getBoundRectanglePixels(this.startCol, this.startRow, col, row);
for(var i = 0; i< strokePoints.length; i++) { for(var i = 0; i< strokePoints.length; i++) {
// Change model: // Change model:
targetFrame.setPixel(strokePoints[i].col, strokePoints[i].row, color); targetFrame.setPixel(strokePoints[i].col, strokePoints[i].row, color);
} }
}; };
})(); })();

View File

@ -4,46 +4,46 @@
* @require pskl.utils * @require pskl.utils
*/ */
(function() { (function() {
var ns = $.namespace("pskl.drawingtools"); var ns = $.namespace("pskl.drawingtools");
ns.SimplePen = function() { ns.SimplePen = function() {
this.toolId = "tool-pen"; this.toolId = "tool-pen";
this.helpText = "Pen tool"; this.helpText = "Pen tool";
this.previousCol = null; this.previousCol = null;
this.previousRow = null; this.previousRow = null;
}; };
pskl.utils.inherit(ns.SimplePen, ns.BaseTool); pskl.utils.inherit(ns.SimplePen, ns.BaseTool);
/** /**
* @override * @override
*/ */
ns.SimplePen.prototype.applyToolAt = function(col, row, color, frame, overlay) { ns.SimplePen.prototype.applyToolAt = function(col, row, color, frame, overlay) {
if (frame.containsPixel(col, row)) { if (frame.containsPixel(col, row)) {
frame.setPixel(col, row, color); frame.setPixel(col, row, color);
} }
this.previousCol = col; this.previousCol = col;
this.previousRow = row; this.previousRow = row;
}; };
ns.SimplePen.prototype.moveToolAt = function(col, row, color, frame, overlay) { ns.SimplePen.prototype.moveToolAt = function(col, row, color, frame, overlay) {
if((Math.abs(col - this.previousCol) > 1) || (Math.abs(row - this.previousRow) > 1)) { 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 // The pen movement is too fast for the mousemove frequency, there is a gap between the
// current point and the previously drawn one. // current point and the previously drawn one.
// We fill the gap by calculating missing dots (simple linear interpolation) and draw them. // We fill the gap by calculating missing dots (simple linear interpolation) and draw them.
var interpolatedPixels = this.getLinePixels_(col, this.previousCol, row, this.previousRow); var interpolatedPixels = this.getLinePixels_(col, this.previousCol, row, this.previousRow);
for(var i=0, l=interpolatedPixels.length; i<l; i++) { for(var i=0, l=interpolatedPixels.length; i<l; i++) {
var coords = interpolatedPixels[i]; var coords = interpolatedPixels[i];
this.applyToolAt(coords.col, coords.row, color, frame, overlay); this.applyToolAt(coords.col, coords.row, color, frame, overlay);
} }
} }
else { else {
this.applyToolAt(col, row, color, frame, overlay); this.applyToolAt(col, row, color, frame, overlay);
} }
this.previousCol = col; this.previousCol = col;
this.previousRow = row; this.previousRow = row;
}; };
})(); })();

View File

@ -4,77 +4,77 @@
* @require pskl.utils * @require pskl.utils
*/ */
(function() { (function() {
var ns = $.namespace("pskl.drawingtools"); var ns = $.namespace("pskl.drawingtools");
ns.Stroke = function() { ns.Stroke = function() {
this.toolId = "tool-stroke"; this.toolId = "tool-stroke";
this.helpText = "Stroke tool"; this.helpText = "Stroke tool";
// Stroke's first point coordinates (set in applyToolAt) // Stroke's first point coordinates (set in applyToolAt)
this.startCol = null; this.startCol = null;
this.startRow = null; this.startRow = null;
}; };
pskl.utils.inherit(ns.Stroke, ns.BaseTool); pskl.utils.inherit(ns.Stroke, ns.BaseTool);
/** /**
* @override * @override
*/ */
ns.Stroke.prototype.applyToolAt = function(col, row, color, frame, overlay) { ns.Stroke.prototype.applyToolAt = function(col, row, color, frame, overlay) {
this.startCol = col; this.startCol = col;
this.startRow = row; this.startRow = row;
// When drawing a stroke we don't change the model instantly, since the // When drawing a stroke we don't change the model instantly, since the
// user can move his cursor to change the stroke direction and length // user can move his cursor to change the stroke direction and length
// dynamically. Instead we draw the (preview) stroke in a fake canvas that // dynamically. Instead we draw the (preview) stroke in a fake canvas that
// overlay the drawing canvas. // overlay the drawing canvas.
// We wait for the releaseToolAt callback to impact both the // We wait for the releaseToolAt callback to impact both the
// frame model and canvas rendering. // frame model and canvas rendering.
// The fake canvas where we will draw the preview of the stroke: // The fake canvas where we will draw the preview of the stroke:
// Drawing the first point of the stroke in the fake overlay canvas: // Drawing the first point of the stroke in the fake overlay canvas:
overlay.setPixel(col, row, color); overlay.setPixel(col, row, color);
}; };
ns.Stroke.prototype.moveToolAt = function(col, row, color, frame, overlay) { ns.Stroke.prototype.moveToolAt = function(col, row, color, frame, overlay) {
overlay.clear(); overlay.clear();
// When the user moussemove (before releasing), we dynamically compute the // When the user moussemove (before releasing), we dynamically compute the
// pixel to draw the line and draw this line in the overlay canvas: // pixel to draw the line and draw this line in the overlay canvas:
var strokePoints = this.getLinePixels_(this.startCol, col, this.startRow, row); var strokePoints = this.getLinePixels_(this.startCol, col, this.startRow, row);
// Drawing current stroke: // Drawing current stroke:
for(var i = 0; i< strokePoints.length; i++) { for(var i = 0; i< strokePoints.length; i++) {
if(color == Constants.TRANSPARENT_COLOR) { if(color == Constants.TRANSPARENT_COLOR) {
// When mousemoving the stroke tool, we draw in the canvas overlay above the drawing canvas. // When mousemoving the stroke tool, we draw in the canvas overlay above the drawing canvas.
// If the stroke color is transparent, we won't be // If the stroke color is transparent, we won't be
// able to see it during the movement. // able to see it during the movement.
// We set it to a semi-opaque white during the tool mousemove allowing to see colors below the stroke. // We set it to a semi-opaque white during the tool mousemove allowing to see colors below the stroke.
// When the stroke tool will be released, It will draw a transparent stroke, // When the stroke tool will be released, It will draw a transparent stroke,
// eg deleting the equivalent of a stroke. // eg deleting the equivalent of a stroke.
color = Constants.SELECTION_TRANSPARENT_COLOR; color = Constants.SELECTION_TRANSPARENT_COLOR;
} }
overlay.setPixel(strokePoints[i].col, strokePoints[i].row, color); overlay.setPixel(strokePoints[i].col, strokePoints[i].row, color);
} }
}; };
/** /**
* @override * @override
*/ */
ns.Stroke.prototype.releaseToolAt = function(col, row, color, frame, overlay) { ns.Stroke.prototype.releaseToolAt = function(col, row, color, frame, overlay) {
// If the stroke tool is released outside of the canvas, we cancel the stroke: // If the stroke tool is released outside of the canvas, we cancel the stroke:
// TODO: Mutualize this check in common method // TODO: Mutualize this check in common method
if(frame.containsPixel(col, row)) { if(frame.containsPixel(col, row)) {
// The user released the tool to draw a line. We will compute the pixel coordinate, impact // The user released the tool to draw a line. We will compute the pixel coordinate, impact
// the model and draw them in the drawing canvas (not the fake overlay anymore) // the model and draw them in the drawing canvas (not the fake overlay anymore)
var strokePoints = this.getLinePixels_(this.startCol, col, this.startRow, row); var strokePoints = this.getLinePixels_(this.startCol, col, this.startRow, row);
for(var i = 0; i< strokePoints.length; i++) { for(var i = 0; i< strokePoints.length; i++) {
// Change model: // Change model:
frame.setPixel(strokePoints[i].col, strokePoints[i].row, color); frame.setPixel(strokePoints[i].col, strokePoints[i].row, color);
} }
} }
// For now, we are done with the stroke tool and don't need an overlay anymore: // For now, we are done with the stroke tool and don't need an overlay anymore:
overlay.clear(); overlay.clear();
}; };
})(); })();

View File

@ -1,46 +1,46 @@
(function() { (function() {
var ns = $.namespace("pskl.drawingtools"); var ns = $.namespace("pskl.drawingtools");
ns.VerticalMirrorPen = function() { ns.VerticalMirrorPen = function() {
this.toolId = "tool-vertical-mirror-pen"; this.toolId = "tool-vertical-mirror-pen";
this.helpText = "vertical mirror pen tool"; this.helpText = "vertical mirror pen tool";
this.swap = null; this.swap = null;
this.mirroredPreviousCol = null; this.mirroredPreviousCol = null;
this.mirroredPreviousRow = null; this.mirroredPreviousRow = null;
}; };
pskl.utils.inherit(ns.VerticalMirrorPen, ns.SimplePen); pskl.utils.inherit(ns.VerticalMirrorPen, ns.SimplePen);
ns.VerticalMirrorPen.prototype.setMirrorContext = function() { ns.VerticalMirrorPen.prototype.setMirrorContext = function() {
this.swap = this.previousCol; this.swap = this.previousCol;
this.previousCol = this.mirroredPreviousCol; this.previousCol = this.mirroredPreviousCol;
}; };
ns.VerticalMirrorPen.prototype.unsetMirrorContext = function() { ns.VerticalMirrorPen.prototype.unsetMirrorContext = function() {
this.mirroredPreviousCol = this.previousCol; this.mirroredPreviousCol = this.previousCol;
this.previousCol = this.swap; this.previousCol = this.swap;
}; };
/** /**
* @override * @override
*/ */
ns.VerticalMirrorPen.prototype.applyToolAt = function(col, row, color, frame, overlay) { ns.VerticalMirrorPen.prototype.applyToolAt = function(col, row, color, frame, overlay) {
this.superclass.applyToolAt.call(this, col, row, color, frame, overlay); this.superclass.applyToolAt.call(this, col, row, color, frame, overlay);
var mirroredCol = this.getSymmetricCol_(col, frame); var mirroredCol = this.getSymmetricCol_(col, frame);
this.mirroredPreviousCol = mirroredCol; this.mirroredPreviousCol = mirroredCol;
this.setMirrorContext(); this.setMirrorContext();
this.superclass.applyToolAt.call(this, mirroredCol, row, color, frame, overlay); this.superclass.applyToolAt.call(this, mirroredCol, row, color, frame, overlay);
this.unsetMirrorContext(); this.unsetMirrorContext();
}; };
/** /**
* @private * @private
*/ */
ns.VerticalMirrorPen.prototype.getSymmetricCol_ = function(col, frame) { ns.VerticalMirrorPen.prototype.getSymmetricCol_ = function(col, frame) {
return frame.getWidth() - col - 1; return frame.getWidth() - col - 1;
}; };
})(); })();

View File

@ -4,147 +4,147 @@
* @require pskl.utils * @require pskl.utils
*/ */
(function() { (function() {
var ns = $.namespace("pskl.drawingtools"); var ns = $.namespace("pskl.drawingtools");
ns.BaseSelect = function() { ns.BaseSelect = function() {
this.secondaryToolId = "tool-move"; this.secondaryToolId = "tool-move";
this.BodyRoot = $('body'); this.BodyRoot = $('body');
// Select's first point coordinates (set in applyToolAt) // Select's first point coordinates (set in applyToolAt)
this.startCol = null; this.startCol = null;
this.startRow = null; this.startRow = null;
}; };
pskl.utils.inherit(ns.BaseSelect, ns.BaseTool); pskl.utils.inherit(ns.BaseSelect, ns.BaseTool);
/** /**
* @override * @override
*/ */
ns.BaseSelect.prototype.applyToolAt = function(col, row, color, frame, overlay) { ns.BaseSelect.prototype.applyToolAt = function(col, row, color, frame, overlay) {
this.startCol = col; this.startCol = col;
this.startRow = row; this.startRow = row;
this.lastCol = col; this.lastCol = col;
this.lastRow = row; this.lastRow = row;
// The select tool can be in two different state. // The select tool can be in two different state.
// If the inital click of the tool is not on a selection, we go in "select" // If the inital click of the tool is not on a selection, we go in "select"
// mode to create a selection. // mode to create a selection.
// If the initial click is on a previous selection, we go in "moveSelection" // If the initial click is on a previous selection, we go in "moveSelection"
// mode to allow to move the selection by drag'n dropping it. // mode to allow to move the selection by drag'n dropping it.
if(overlay.getPixel(col, row) != Constants.SELECTION_TRANSPARENT_COLOR) { if(overlay.getPixel(col, row) != Constants.SELECTION_TRANSPARENT_COLOR) {
this.mode = "select"; this.mode = "select";
this.onSelectStart_(col, row, color, frame, overlay); this.onSelectStart_(col, row, color, frame, overlay);
} }
else { else {
this.mode = "moveSelection"; this.mode = "moveSelection";
this.onSelectionDragStart_(col, row, color, frame, overlay); this.onSelectionDragStart_(col, row, color, frame, overlay);
} }
}; };
/** /**
* @override * @override
*/ */
ns.BaseSelect.prototype.moveToolAt = function(col, row, color, frame, overlay) { ns.BaseSelect.prototype.moveToolAt = function(col, row, color, frame, overlay) {
if(this.mode == "select") { if(this.mode == "select") {
this.onSelect_(col, row, color, frame, overlay); this.onSelect_(col, row, color, frame, overlay);
} }
else if(this.mode == "moveSelection") { else if(this.mode == "moveSelection") {
this.onSelectionDrag_(col, row, color, frame, overlay); this.onSelectionDrag_(col, row, color, frame, overlay);
} }
}; };
/** /**
* @override * @override
*/ */
ns.BaseSelect.prototype.releaseToolAt = function(col, row, color, frame, overlay) { ns.BaseSelect.prototype.releaseToolAt = function(col, row, color, frame, overlay) {
if(this.mode == "select") { if(this.mode == "select") {
this.onSelectEnd_(col, row, color, frame, overlay); this.onSelectEnd_(col, row, color, frame, overlay);
} else if(this.mode == "moveSelection") { } else if(this.mode == "moveSelection") {
this.onSelectionDragEnd_(col, row, color, frame, overlay); this.onSelectionDragEnd_(col, row, color, frame, overlay);
} }
}; };
/** /**
* If we mouseover the selection draw inside the overlay frame, show the 'move' cursor * If we mouseover the selection draw inside the overlay frame, show the 'move' cursor
* instead of the 'select' one. It indicates that we can move the selection by dragndroping it. * instead of the 'select' one. It indicates that we can move the selection by dragndroping it.
* @override * @override
*/ */
ns.BaseSelect.prototype.moveUnactiveToolAt = function(col, row, color, frame, overlay) { ns.BaseSelect.prototype.moveUnactiveToolAt = function(col, row, color, frame, overlay) {
if(overlay.getPixel(col, row) != Constants.SELECTION_TRANSPARENT_COLOR) { if(overlay.getPixel(col, row) != Constants.SELECTION_TRANSPARENT_COLOR) {
// We're hovering the selection, show the move tool: // We're hovering the selection, show the move tool:
this.BodyRoot.addClass(this.toolId); this.BodyRoot.addClass(this.toolId);
this.BodyRoot.removeClass(this.secondaryToolId); this.BodyRoot.removeClass(this.secondaryToolId);
} else { } else {
// We're not hovering the selection, show create selection tool: // We're not hovering the selection, show create selection tool:
this.BodyRoot.addClass(this.secondaryToolId); this.BodyRoot.addClass(this.secondaryToolId);
this.BodyRoot.removeClass(this.toolId); this.BodyRoot.removeClass(this.toolId);
} }
}; };
/** /**
* Move the overlay frame filled with semi-transparent pixels that represent the selection. * Move the overlay frame filled with semi-transparent pixels that represent the selection.
* @private * @private
*/ */
ns.BaseSelect.prototype.shiftOverlayFrame_ = function (colDiff, rowDiff, overlayFrame, reference) { ns.BaseSelect.prototype.shiftOverlayFrame_ = function (colDiff, rowDiff, overlayFrame, reference) {
var color; var color;
for (var col = 0 ; col < overlayFrame.getWidth() ; col++) { for (var col = 0 ; col < overlayFrame.getWidth() ; col++) {
for (var row = 0 ; row < overlayFrame.getHeight() ; row++) { for (var row = 0 ; row < overlayFrame.getHeight() ; row++) {
if (reference.containsPixel(col - colDiff, row - rowDiff)) { if (reference.containsPixel(col - colDiff, row - rowDiff)) {
color = reference.getPixel(col - colDiff, row - rowDiff); color = reference.getPixel(col - colDiff, row - rowDiff);
} else { } else {
color = Constants.TRANSPARENT_COLOR; color = Constants.TRANSPARENT_COLOR;
} }
overlayFrame.setPixel(col, row, color); overlayFrame.setPixel(col, row, color);
} }
} }
}; };
// The list of callbacks to implement by specialized tools to implement the selection creation behavior. // The list of callbacks to implement by specialized tools to implement the selection creation behavior.
/** @protected */ /** @protected */
ns.BaseSelect.prototype.onSelectStart_ = function (col, row, color, frame, overlay) {}; ns.BaseSelect.prototype.onSelectStart_ = function (col, row, color, frame, overlay) {};
/** @protected */ /** @protected */
ns.BaseSelect.prototype.onSelect_ = function (col, row, color, frame, overlay) {}; ns.BaseSelect.prototype.onSelect_ = function (col, row, color, frame, overlay) {};
/** @protected */ /** @protected */
ns.BaseSelect.prototype.onSelectEnd_ = function (col, row, color, frame, overlay) {}; ns.BaseSelect.prototype.onSelectEnd_ = function (col, row, color, frame, overlay) {};
// The list of callbacks that define the drag'n drop behavior of the selection. // The list of callbacks that define the drag'n drop behavior of the selection.
/** @private */ /** @private */
ns.BaseSelect.prototype.onSelectionDragStart_ = function (col, row, color, frame, overlay) { ns.BaseSelect.prototype.onSelectionDragStart_ = function (col, row, color, frame, overlay) {
// Since we will move the overlayFrame in which the current selection is rendered, // Since we will move the overlayFrame in which the current selection is rendered,
// we clone it to have a reference for the later shifting process. // we clone it to have a reference for the later shifting process.
this.overlayFrameReference = overlay.clone(); this.overlayFrameReference = overlay.clone();
}; };
/** @private */ /** @private */
ns.BaseSelect.prototype.onSelectionDrag_ = function (col, row, color, frame, overlay) { ns.BaseSelect.prototype.onSelectionDrag_ = function (col, row, color, frame, overlay) {
var deltaCol = col - this.lastCol; var deltaCol = col - this.lastCol;
var deltaRow = row - this.lastRow; var deltaRow = row - this.lastRow;
var colDiff = col - this.startCol, rowDiff = row - this.startRow; var colDiff = col - this.startCol, rowDiff = row - this.startRow;
// Shifting selection on overlay frame: // Shifting selection on overlay frame:
this.shiftOverlayFrame_(colDiff, rowDiff, overlay, this.overlayFrameReference); this.shiftOverlayFrame_(colDiff, rowDiff, overlay, this.overlayFrameReference);
// Update selection model: // Update selection model:
$.publish(Events.SELECTION_MOVE_REQUEST, [deltaCol, deltaRow]); $.publish(Events.SELECTION_MOVE_REQUEST, [deltaCol, deltaRow]);
this.lastCol = col; this.lastCol = col;
this.lastRow = row; this.lastRow = row;
}; };
/** @private */ /** @private */
ns.BaseSelect.prototype.onSelectionDragEnd_ = function (col, row, color, frame, overlay) { ns.BaseSelect.prototype.onSelectionDragEnd_ = function (col, row, color, frame, overlay) {
this.onSelectionDrag_(col, row, color, frame, overlay); this.onSelectionDrag_(col, row, color, frame, overlay);
}; };
})(); })();

View File

@ -4,48 +4,48 @@
* @require pskl.utils * @require pskl.utils
*/ */
(function() { (function() {
var ns = $.namespace("pskl.drawingtools"); var ns = $.namespace("pskl.drawingtools");
ns.RectangleSelect = function() { ns.RectangleSelect = function() {
this.toolId = "tool-rectangle-select"; this.toolId = "tool-rectangle-select";
this.helpText = "Rectangle selection tool"; this.helpText = "Rectangle selection tool";
ns.BaseSelect.call(this); ns.BaseSelect.call(this);
}; };
pskl.utils.inherit(ns.RectangleSelect, ns.BaseSelect); pskl.utils.inherit(ns.RectangleSelect, ns.BaseSelect);
/** /**
* @override * @override
*/ */
ns.RectangleSelect.prototype.onSelectStart_ = function (col, row, color, frame, overlay) { ns.RectangleSelect.prototype.onSelectStart_ = function (col, row, color, frame, overlay) {
// Drawing the first point of the rectangle in the fake overlay canvas: // Drawing the first point of the rectangle in the fake overlay canvas:
overlay.setPixel(col, row, color); overlay.setPixel(col, row, color);
}; };
/** /**
* When creating the rectangle selection, we clear the current overlayFrame and * When creating the rectangle selection, we clear the current overlayFrame and
* redraw the current rectangle based on the orgin coordinate and * redraw the current rectangle based on the orgin coordinate and
* the current mouse coordiinate in sprite. * the current mouse coordiinate in sprite.
* @override * @override
*/ */
ns.RectangleSelect.prototype.onSelect_ = function (col, row, color, frame, overlay) { ns.RectangleSelect.prototype.onSelect_ = function (col, row, color, frame, overlay) {
overlay.clear(); overlay.clear();
if(this.startCol == col &&this.startRow == row) { if(this.startCol == col &&this.startRow == row) {
$.publish(Events.SELECTION_DISMISSED); $.publish(Events.SELECTION_DISMISSED);
} else { } else {
var selection = new pskl.selection.RectangularSelection( var selection = new pskl.selection.RectangularSelection(
this.startCol, this.startRow, col, row); this.startCol, this.startRow, col, row);
$.publish(Events.SELECTION_CREATED, [selection]); $.publish(Events.SELECTION_CREATED, [selection]);
} }
}; };
/** /**
* @override * @override
*/ */
ns.RectangleSelect.prototype.onSelectEnd_ = function (col, row, color, frame, overlay) { ns.RectangleSelect.prototype.onSelectEnd_ = function (col, row, color, frame, overlay) {
this.onSelect_(col, row, color, frame, overlay); this.onSelect_(col, row, color, frame, overlay);
}; };
})(); })();

View File

@ -4,31 +4,31 @@
* @require pskl.utils * @require pskl.utils
*/ */
(function() { (function() {
var ns = $.namespace("pskl.drawingtools"); var ns = $.namespace("pskl.drawingtools");
ns.ShapeSelect = function() { ns.ShapeSelect = function() {
this.toolId = "tool-shape-select"; this.toolId = "tool-shape-select";
this.helpText = "Shape selection tool"; this.helpText = "Shape selection tool";
ns.BaseSelect.call(this); ns.BaseSelect.call(this);
}; };
pskl.utils.inherit(ns.ShapeSelect, ns.BaseSelect); pskl.utils.inherit(ns.ShapeSelect, ns.BaseSelect);
/** /**
* For the shape select tool, you just need to click one time to create a selection. * For the shape select tool, you just need to click one time to create a selection.
* So we jsut need to implement onSelectStart_ (no need for onSelect_ & onSelectEnd_) * So we jsut need to implement onSelectStart_ (no need for onSelect_ & onSelectEnd_)
* @override * @override
*/ */
ns.ShapeSelect.prototype.onSelectStart_ = function (col, row, color, frame, overlay) { ns.ShapeSelect.prototype.onSelectStart_ = function (col, row, color, frame, overlay) {
// Clean previous selection: // Clean previous selection:
$.publish(Events.SELECTION_DISMISSED); $.publish(Events.SELECTION_DISMISSED);
// From the pixel cliked, get shape using an algorithm similar to the paintbucket one: // From the pixel cliked, get shape using an algorithm similar to the paintbucket one:
var pixels = pskl.PixelUtils.getSimilarConnectedPixelsFromFrame(frame, col, row); var pixels = pskl.PixelUtils.getSimilarConnectedPixelsFromFrame(frame, col, row);
var selection = new pskl.selection.ShapeSelection(pixels); var selection = new pskl.selection.ShapeSelection(pixels);
$.publish(Events.SELECTION_CREATED, [selection]); $.publish(Events.SELECTION_CREATED, [selection]);
}; };
})(); })();

View File

@ -1,118 +1,118 @@
(function () { (function () {
var ns = $.namespace("pskl.model"); var ns = $.namespace("pskl.model");
ns.Frame = function (pixels) { ns.Frame = function (pixels) {
this.pixels = pixels; this.pixels = pixels;
this.previousStates = [this.getPixels()]; this.previousStates = [this.getPixels()];
this.stateIndex = 0; this.stateIndex = 0;
}; };
ns.Frame.createEmpty = function (width, height) { ns.Frame.createEmpty = function (width, height) {
var pixels = ns.Frame.createEmptyPixelGrid_(width, height); var pixels = ns.Frame.createEmptyPixelGrid_(width, height);
return new ns.Frame(pixels); return new ns.Frame(pixels);
}; };
ns.Frame.createEmptyPixelGrid_ = function (width, height) { ns.Frame.createEmptyPixelGrid_ = function (width, height) {
var pixels = []; //new Array(width); var pixels = []; //new Array(width);
for (var columnIndex=0; columnIndex < width; columnIndex++) { for (var columnIndex=0; columnIndex < width; columnIndex++) {
var columnArray = []; var columnArray = [];
for(var heightIndex = 0; heightIndex < height; heightIndex++) { for(var heightIndex = 0; heightIndex < height; heightIndex++) {
columnArray.push(Constants.TRANSPARENT_COLOR); columnArray.push(Constants.TRANSPARENT_COLOR);
} }
pixels[columnIndex] = columnArray; pixels[columnIndex] = columnArray;
} }
return pixels; return pixels;
}; };
ns.Frame.createEmptyFromFrame = function (frame) { ns.Frame.createEmptyFromFrame = function (frame) {
return ns.Frame.createEmpty(frame.getWidth(), frame.getHeight()); return ns.Frame.createEmpty(frame.getWidth(), frame.getHeight());
}; };
ns.Frame.prototype.clone = function () { ns.Frame.prototype.clone = function () {
return new ns.Frame(this.getPixels()); return new ns.Frame(this.getPixels());
}; };
/** /**
* Returns a copy of the pixels used by the frame * Returns a copy of the pixels used by the frame
*/ */
ns.Frame.prototype.getPixels = function () { ns.Frame.prototype.getPixels = function () {
return this.clonePixels_(this.pixels); return this.clonePixels_(this.pixels);
}; };
/** /**
* Copies the passed pixels into the frame. * Copies the passed pixels into the frame.
*/ */
ns.Frame.prototype.setPixels = function (pixels) { ns.Frame.prototype.setPixels = function (pixels) {
this.pixels = this.clonePixels_(pixels); this.pixels = this.clonePixels_(pixels);
}; };
ns.Frame.prototype.clear = function () { ns.Frame.prototype.clear = function () {
var pixels = ns.Frame.createEmptyPixelGrid_(this.getWidth(), this.getHeight()); var pixels = ns.Frame.createEmptyPixelGrid_(this.getWidth(), this.getHeight());
this.setPixels(pixels); this.setPixels(pixels);
}; };
/** /**
* Clone a set of pixels. Should be static utility method * Clone a set of pixels. Should be static utility method
* @private * @private
*/ */
ns.Frame.prototype.clonePixels_ = function (pixels) { ns.Frame.prototype.clonePixels_ = function (pixels) {
var clonedPixels = []; var clonedPixels = [];
for (var col = 0 ; col < pixels.length ; col++) { for (var col = 0 ; col < pixels.length ; col++) {
clonedPixels[col] = pixels[col].slice(0 , pixels[col].length); clonedPixels[col] = pixels[col].slice(0 , pixels[col].length);
} }
return clonedPixels; return clonedPixels;
}; };
ns.Frame.prototype.serialize = function () { ns.Frame.prototype.serialize = function () {
return JSON.stringify(this.pixels); return JSON.stringify(this.pixels);
}; };
ns.Frame.prototype.setPixel = function (col, row, color) { ns.Frame.prototype.setPixel = function (col, row, color) {
this.pixels[col][row] = color; this.pixels[col][row] = color;
}; };
ns.Frame.prototype.getPixel = function (col, row) { ns.Frame.prototype.getPixel = function (col, row) {
return this.pixels[col][row]; return this.pixels[col][row];
}; };
ns.Frame.prototype.getWidth = function () { ns.Frame.prototype.getWidth = function () {
return this.pixels.length; return this.pixels.length;
}; };
ns.Frame.prototype.getHeight = function () { ns.Frame.prototype.getHeight = function () {
return this.pixels[0].length; return this.pixels[0].length;
}; };
ns.Frame.prototype.containsPixel = function (col, row) { ns.Frame.prototype.containsPixel = function (col, row) {
return col >= 0 && row >= 0 && col < this.pixels.length && row < this.pixels[0].length; return col >= 0 && row >= 0 && col < this.pixels.length && row < this.pixels[0].length;
}; };
ns.Frame.prototype.saveState = function () { ns.Frame.prototype.saveState = function () {
// remove all states past current state // remove all states past current state
this.previousStates.length = this.stateIndex + 1; this.previousStates.length = this.stateIndex + 1;
// push new state // push new state
this.previousStates.push(this.getPixels()); this.previousStates.push(this.getPixels());
// set the stateIndex to latest saved state // set the stateIndex to latest saved state
this.stateIndex = this.previousStates.length - 1; this.stateIndex = this.previousStates.length - 1;
}; };
ns.Frame.prototype.loadPreviousState = function () { ns.Frame.prototype.loadPreviousState = function () {
if (this.stateIndex > 0) { if (this.stateIndex > 0) {
this.stateIndex--; this.stateIndex--;
this.setPixels(this.previousStates[this.stateIndex]); this.setPixels(this.previousStates[this.stateIndex]);
} }
}; };
ns.Frame.prototype.loadNextState = function () { ns.Frame.prototype.loadNextState = function () {
if (this.stateIndex < this.previousStates.length - 1) { if (this.stateIndex < this.previousStates.length - 1) {
this.stateIndex++; this.stateIndex++;
this.setPixels(this.previousStates[this.stateIndex]); this.setPixels(this.previousStates[this.stateIndex]);
} }
}; };
ns.Frame.prototype.isSameSize = function (otherFrame) { ns.Frame.prototype.isSameSize = function (otherFrame) {
return this.getHeight() == otherFrame.getHeight() && this.getWidth() == otherFrame.getWidth(); return this.getHeight() == otherFrame.getHeight() && this.getWidth() == otherFrame.getWidth();
}; };
})(); })();

View File

@ -1,157 +1,157 @@
(function () { (function () {
var ns = $.namespace("pskl.model"); var ns = $.namespace("pskl.model");
ns.FrameSheet = function (height, width) { ns.FrameSheet = function (height, width) {
this.width = width; this.width = width;
this.height = height; this.height = height;
this.frames = []; this.frames = [];
this.currentFrameIndex = 0; this.currentFrameIndex = 0;
}; };
ns.FrameSheet.prototype.getHeight = function () { ns.FrameSheet.prototype.getHeight = function () {
return this.height; return this.height;
}; };
ns.FrameSheet.prototype.getWidth = function () { ns.FrameSheet.prototype.getWidth = function () {
return this.width; return this.width;
}; };
ns.FrameSheet.prototype.addEmptyFrame = function () { ns.FrameSheet.prototype.addEmptyFrame = function () {
this.addFrame(ns.Frame.createEmpty(this.width, this.height)); this.addFrame(ns.Frame.createEmpty(this.width, this.height));
}; };
ns.FrameSheet.prototype.addFrame = function (frame) { ns.FrameSheet.prototype.addFrame = function (frame) {
this.frames.push(frame); this.frames.push(frame);
}; };
ns.FrameSheet.prototype.getFrameCount = function () { ns.FrameSheet.prototype.getFrameCount = function () {
return this.frames.length; return this.frames.length;
}; };
ns.FrameSheet.prototype.getCurrentFrame = function () { ns.FrameSheet.prototype.getCurrentFrame = function () {
return this.frames[this.currentFrameIndex]; return this.frames[this.currentFrameIndex];
}; };
ns.FrameSheet.prototype.setCurrentFrameIndex = function (index) { ns.FrameSheet.prototype.setCurrentFrameIndex = function (index) {
this.currentFrameIndex = index; this.currentFrameIndex = index;
$.publish(Events.CURRENT_FRAME_SET, [this.getCurrentFrame()]); $.publish(Events.CURRENT_FRAME_SET, [this.getCurrentFrame()]);
$.publish(Events.FRAMESHEET_RESET); // Is it no to overkill to have this here ? $.publish(Events.FRAMESHEET_RESET); // Is it no to overkill to have this here ?
}; };
ns.FrameSheet.prototype.getUsedColors = function() { ns.FrameSheet.prototype.getUsedColors = function() {
var colors = {}; var colors = {};
for (var frameIndex=0; frameIndex < this.frames.length; frameIndex++) { for (var frameIndex=0; frameIndex < this.frames.length; frameIndex++) {
var frame = this.frames[frameIndex]; var frame = this.frames[frameIndex];
for (var i = 0, width = frame.getWidth(); i < width ; i++) { for (var i = 0, width = frame.getWidth(); i < width ; i++) {
var line = frame[i]; var line = frame[i];
for (var j = 0, height = frame.getHeight() ; j < height ; j++) { for (var j = 0, height = frame.getHeight() ; j < height ; j++) {
var pixel = frame.getPixel(i, j); var pixel = frame.getPixel(i, j);
colors[pixel] = pixel; colors[pixel] = pixel;
} }
} }
} }
return colors; return colors;
}; };
// Could be used to pass around model using long GET param (good enough for simple models) and // Could be used to pass around model using long GET param (good enough for simple models) and
// do some temporary locastorage // do some temporary locastorage
ns.FrameSheet.prototype.serialize = function() { ns.FrameSheet.prototype.serialize = function() {
var serializedFrames = []; var serializedFrames = [];
for (var i = 0 ; i < this.frames.length ; i++) { for (var i = 0 ; i < this.frames.length ; i++) {
serializedFrames.push(this.frames[i].serialize()); serializedFrames.push(this.frames[i].serialize());
} }
return '[' + serializedFrames.join(",") + ']'; return '[' + serializedFrames.join(",") + ']';
//return JSON.stringify(frames); //return JSON.stringify(frames);
}; };
/** /**
* Load a framesheet from a model that might have been persisted in db / localstorage * Load a framesheet from a model that might have been persisted in db / localstorage
* Overrides existing frames. * Overrides existing frames.
* @param {String} serialized * @param {String} serialized
*/ */
ns.FrameSheet.prototype.deserialize = function (serialized) { ns.FrameSheet.prototype.deserialize = function (serialized) {
try { try {
this.load(JSON.parse(serialized)); this.load(JSON.parse(serialized));
} catch (e) { } catch (e) {
throw "Could not load serialized framesheet : " + e.message; throw "Could not load serialized framesheet : " + e.message;
} }
}; };
/** /**
* Load a framesheet from a model that might have been persisted in db / localstorage * Load a framesheet from a model that might have been persisted in db / localstorage
* Overrides existing frames. * Overrides existing frames.
* @param {String} serialized * @param {String} serialized
*/ */
ns.FrameSheet.prototype.load = function (framesheet) { ns.FrameSheet.prototype.load = function (framesheet) {
this.frames = []; this.frames = [];
for (var i = 0 ; i < framesheet.length ; i++) { for (var i = 0 ; i < framesheet.length ; i++) {
var frameCfg = framesheet[i]; var frameCfg = framesheet[i];
this.addFrame(new ns.Frame(frameCfg)); this.addFrame(new ns.Frame(frameCfg));
} }
if (this.hasFrameAtIndex(0)) { if (this.hasFrameAtIndex(0)) {
this.height = this.getFrameByIndex(0).getHeight(); this.height = this.getFrameByIndex(0).getHeight();
this.width = this.getFrameByIndex(0).getWidth(); this.width = this.getFrameByIndex(0).getWidth();
this.setCurrentFrameIndex(0); this.setCurrentFrameIndex(0);
$.publish(Events.FRAME_SIZE_CHANGED); $.publish(Events.FRAME_SIZE_CHANGED);
} }
$.publish(Events.FRAMESHEET_RESET); $.publish(Events.FRAMESHEET_RESET);
}; };
ns.FrameSheet.prototype.hasFrameAtIndex = function(index) { ns.FrameSheet.prototype.hasFrameAtIndex = function(index) {
return (index >= 0 && index < this.getFrameCount()); return (index >= 0 && index < this.getFrameCount());
}; };
ns.FrameSheet.prototype.getFrameByIndex = function(index) { ns.FrameSheet.prototype.getFrameByIndex = function(index) {
if (isNaN(index)) { if (isNaN(index)) {
throw "Bad argument value for getFrameByIndex method: <" + index + ">"; throw "Bad argument value for getFrameByIndex method: <" + index + ">";
} }
if (!this.hasFrameAtIndex(index)) { if (!this.hasFrameAtIndex(index)) {
throw "Out of bound index for frameSheet object."; throw "Out of bound index for frameSheet object.";
} }
return this.frames[index]; return this.frames[index];
}; };
ns.FrameSheet.prototype.removeFrameByIndex = function(index) { ns.FrameSheet.prototype.removeFrameByIndex = function(index) {
if(!this.hasFrameAtIndex(index)) { if(!this.hasFrameAtIndex(index)) {
throw "Out of bound index for frameSheet object."; throw "Out of bound index for frameSheet object.";
} }
this.frames.splice(index, 1); this.frames.splice(index, 1);
// Current frame index might not be valid anymore // Current frame index might not be valid anymore
if (!this.hasFrameAtIndex(this.currentFrameIndex)) { if (!this.hasFrameAtIndex(this.currentFrameIndex)) {
// if not select last frame available // if not select last frame available
this.setCurrentFrameIndex(this.getFrameCount() - 1); this.setCurrentFrameIndex(this.getFrameCount() - 1);
} }
$.publish(Events.FRAMESHEET_RESET); $.publish(Events.FRAMESHEET_RESET);
}; };
ns.FrameSheet.prototype.duplicateFrameByIndex = function(index) { ns.FrameSheet.prototype.duplicateFrameByIndex = function(index) {
var frame = this.getFrameByIndex(index); var frame = this.getFrameByIndex(index);
this.frames.splice(index + 1, 0, frame.clone()); this.frames.splice(index + 1, 0, frame.clone());
}; };
ns.FrameSheet.prototype.moveFrame = function(originIndex, destinationIndex) { ns.FrameSheet.prototype.moveFrame = function(originIndex, destinationIndex) {
this.frames.splice(destinationIndex, 0, this.frames.splice(originIndex, 1)[0]); this.frames.splice(destinationIndex, 0, this.frames.splice(originIndex, 1)[0]);
}; };
ns.FrameSheet.prototype.swapFrames = function(indexFrame1, indexFrame2) { ns.FrameSheet.prototype.swapFrames = function(indexFrame1, indexFrame2) {
if(isNaN(indexFrame1) || isNaN(indexFrame1) || if(isNaN(indexFrame1) || isNaN(indexFrame1) ||
(!this.hasFrameAtIndex(indexFrame1) && !this.hasFrameAtIndex(indexFrame2))) { (!this.hasFrameAtIndex(indexFrame1) && !this.hasFrameAtIndex(indexFrame2))) {
throw "Bad indexes for swapFrames Framesheet function."; throw "Bad indexes for swapFrames Framesheet function.";
} }
if(indexFrame1 == indexFrame2) { if(indexFrame1 == indexFrame2) {
return; return;
} }
else { else {
var swapFrame = this.frames[indexFrame1]; var swapFrame = this.frames[indexFrame1];
this.frames[indexFrame1] = this.frames[indexFrame2]; this.frames[indexFrame1] = this.frames[indexFrame2];
this.frames[indexFrame2] = swapFrame; this.frames[indexFrame2] = swapFrame;
} }
}; };
})(); })();

View File

@ -184,7 +184,7 @@
if (this.status == 200) { if (this.status == 200) {
if (pskl.app.isStaticVersion) { if (pskl.app.isStaticVersion) {
var baseUrl = window.location.href.replace(window.location.search, ""); var baseUrl = window.location.href.replace(window.location.search, "");
window.location.href = baseUrl + "?frameId=" + this.responseText; window.location.href = baseUrl + "?frameId=" + this.responseText;
} else { } else {
$.publish(Events.SHOW_NOTIFICATION, [{"content": "Successfully saved !"}]); $.publish(Events.SHOW_NOTIFICATION, [{"content": "Successfully saved !"}]);
} }

View File

@ -1,36 +1,36 @@
(function () { (function () {
var ns = $.namespace("pskl.rendering"); var ns = $.namespace("pskl.rendering");
ns.CanvasRenderer = function (frame, dpi) { ns.CanvasRenderer = function (frame, dpi) {
this.frame = frame; this.frame = frame;
this.dpi = dpi; this.dpi = dpi;
}; };
ns.CanvasRenderer.prototype.render = function (frame, dpi) { ns.CanvasRenderer.prototype.render = function (frame, dpi) {
var canvas = this.createCanvas_(); var canvas = this.createCanvas_();
var context = canvas.getContext('2d'); var context = canvas.getContext('2d');
for(var col = 0, width = this.frame.getWidth(); col < width; col++) { for(var col = 0, width = this.frame.getWidth(); col < width; col++) {
for(var row = 0, height = this.frame.getHeight(); row < height; row++) { for(var row = 0, height = this.frame.getHeight(); row < height; row++) {
var color = this.frame.getPixel(col, row); var color = this.frame.getPixel(col, row);
this.renderPixel_(color, col, row, context); this.renderPixel_(color, col, row, context);
} }
} }
return context; return context;
}; };
ns.CanvasRenderer.prototype.renderPixel_ = function (color, col, row, context) { ns.CanvasRenderer.prototype.renderPixel_ = function (color, col, row, context) {
if(color == Constants.TRANSPARENT_COLOR) { if(color == Constants.TRANSPARENT_COLOR) {
color = "#FFF"; color = "#FFF";
} }
context.fillStyle = color; context.fillStyle = color;
context.fillRect(col * this.dpi, row * this.dpi, this.dpi, this.dpi); context.fillRect(col * this.dpi, row * this.dpi, this.dpi, this.dpi);
}; };
ns.CanvasRenderer.prototype.createCanvas_ = function () { ns.CanvasRenderer.prototype.createCanvas_ = function () {
var width = this.frame.getWidth() * this.dpi; var width = this.frame.getWidth() * this.dpi;
var height = this.frame.getHeight() * this.dpi; var height = this.frame.getHeight() * this.dpi;
return pskl.CanvasUtils.createCanvas(width, height); return pskl.CanvasUtils.createCanvas(width, height);
}; };
})(); })();

View File

@ -1,61 +1,61 @@
(function () { (function () {
var ns = $.namespace("pskl.rendering"); var ns = $.namespace("pskl.rendering");
ns.DrawingLoop = function () { ns.DrawingLoop = function () {
this.requestAnimationFrame = this.getRequestAnimationFrameShim_(); this.requestAnimationFrame = this.getRequestAnimationFrameShim_();
this.isRunning = false; this.isRunning = false;
this.previousTime = 0; this.previousTime = 0;
this.callbacks = []; this.callbacks = [];
}; };
ns.DrawingLoop.prototype.addCallback = function (callback, scope, args) { ns.DrawingLoop.prototype.addCallback = function (callback, scope, args) {
var callbackObj = { var callbackObj = {
fn : callback, fn : callback,
scope : scope, scope : scope,
args : args args : args
}; };
this.callbacks.push(callbackObj); this.callbacks.push(callbackObj);
return callbackObj; return callbackObj;
}; };
ns.DrawingLoop.prototype.removeCallback = function (callbackObj) { ns.DrawingLoop.prototype.removeCallback = function (callbackObj) {
var index = this.callbacks.indexOf(callbackObj); var index = this.callbacks.indexOf(callbackObj);
if (index != -1) { if (index != -1) {
this.callbacks.splice(index, 1); this.callbacks.splice(index, 1);
} }
}; };
ns.DrawingLoop.prototype.start = function () { ns.DrawingLoop.prototype.start = function () {
this.isRunning = true; this.isRunning = true;
this.loop_(); this.loop_();
}; };
ns.DrawingLoop.prototype.loop_ = function () { ns.DrawingLoop.prototype.loop_ = function () {
var currentTime = Date.now(); var currentTime = Date.now();
var delta = currentTime - this.previousTime; var delta = currentTime - this.previousTime;
this.executeCallbacks_(delta); this.executeCallbacks_(delta);
this.previousTime = currentTime; this.previousTime = currentTime;
this.requestAnimationFrame.call(window, this.loop_.bind(this)); this.requestAnimationFrame.call(window, this.loop_.bind(this));
}; };
ns.DrawingLoop.prototype.executeCallbacks_ = function (deltaTime) { ns.DrawingLoop.prototype.executeCallbacks_ = function (deltaTime) {
for (var i = 0 ; i < this.callbacks.length ; i++) { for (var i = 0 ; i < this.callbacks.length ; i++) {
var cb = this.callbacks[i]; var cb = this.callbacks[i];
cb.fn.call(cb.scope, deltaTime, cb.args); cb.fn.call(cb.scope, deltaTime, cb.args);
} }
}; };
ns.DrawingLoop.prototype.stop = function () { ns.DrawingLoop.prototype.stop = function () {
this.isRunning = false; this.isRunning = false;
}; };
ns.DrawingLoop.prototype.getRequestAnimationFrameShim_ = function () { ns.DrawingLoop.prototype.getRequestAnimationFrameShim_ = function () {
var requestAnimationFrame = window.requestAnimationFrame || var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame || window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame || window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame || window.msRequestAnimationFrame ||
function (callback) { window.setTimeout(callback, 1000/60); }; function (callback) { window.setTimeout(callback, 1000/60); };
return requestAnimationFrame; return requestAnimationFrame;
}; };
})(); })();

View File

@ -1,165 +1,165 @@
(function () { (function () {
var ns = $.namespace("pskl.rendering"); var ns = $.namespace("pskl.rendering");
ns.FrameRenderer = function (container, renderingOptions, className) { ns.FrameRenderer = function (container, renderingOptions, className) {
this.defaultRenderingOptions = { this.defaultRenderingOptions = {
'supportGridRendering' : false 'supportGridRendering' : false
}; };
renderingOptions = $.extend(true, {}, this.defaultRenderingOptions, renderingOptions); renderingOptions = $.extend(true, {}, this.defaultRenderingOptions, renderingOptions);
if(container === undefined) { if(container === undefined) {
throw 'Bad FrameRenderer initialization. <container> undefined.'; throw 'Bad FrameRenderer initialization. <container> undefined.';
} }
if(isNaN(renderingOptions.dpi)) {
throw 'Bad FrameRenderer initialization. <dpi> 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);
}
if(isNaN(renderingOptions.dpi)) { this.canvas = canvas;
throw 'Bad FrameRenderer initialization. <dpi> not well defined.'; this.canvasConfigDirty = false;
} }
return this.canvas;
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;
};
})(); })();

View File

@ -1,63 +1,63 @@
(function () { (function () {
var ns = $.namespace("pskl.rendering"); var ns = $.namespace("pskl.rendering");
ns.SpritesheetRenderer = function (framesheet) { ns.SpritesheetRenderer = function (framesheet) {
this.framesheet = framesheet; this.framesheet = framesheet;
}; };
ns.SpritesheetRenderer.prototype.renderAsImageDataSpritesheetPNG = function () { ns.SpritesheetRenderer.prototype.renderAsImageDataSpritesheetPNG = function () {
var canvas = this.createCanvas_(); var canvas = this.createCanvas_();
for (var i = 0 ; i < this.framesheet.getFrameCount() ; i++) { for (var i = 0 ; i < this.framesheet.getFrameCount() ; i++) {
var frame = this.framesheet.getFrameByIndex(i); var frame = this.framesheet.getFrameByIndex(i);
this.drawFrameInCanvas_(frame, canvas, i * this.framesheet.getWidth(), 0); this.drawFrameInCanvas_(frame, canvas, i * this.framesheet.getWidth(), 0);
} }
return canvas.toDataURL("image/png"); return canvas.toDataURL("image/png");
}; };
ns.SpritesheetRenderer.prototype.renderAsImageDataAnimatedGIF = function (fps) { ns.SpritesheetRenderer.prototype.renderAsImageDataAnimatedGIF = function (fps) {
var encoder = new GIFEncoder(), dpi = 10; var encoder = new GIFEncoder(), dpi = 10;
encoder.setRepeat(0); encoder.setRepeat(0);
encoder.setDelay(1000/fps); encoder.setDelay(1000/fps);
encoder.start(); encoder.start();
encoder.setSize(this.framesheet.getWidth() * dpi, this.framesheet.getHeight() * dpi); encoder.setSize(this.framesheet.getWidth() * dpi, this.framesheet.getHeight() * dpi);
for (var i = 0 ; i < this.framesheet.frames.length ; i++) { for (var i = 0 ; i < this.framesheet.frames.length ; i++) {
var frame = this.framesheet.frames[i]; var frame = this.framesheet.frames[i];
var renderer = new pskl.rendering.CanvasRenderer(frame, dpi); var renderer = new pskl.rendering.CanvasRenderer(frame, dpi);
encoder.addFrame(renderer.render()); encoder.addFrame(renderer.render());
}
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);
} }
encoder.finish(); }
}
};
var imageData = 'data:image/gif;base64,' + encode64(encoder.stream().getData()); ns.SpritesheetRenderer.prototype.createCanvas_ = function () {
return imageData; 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);
* TODO(juliandescottes): Mutualize with code already present in FrameRenderer } else {
*/ throw "Cannot render empty Spritesheet";
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";
}
};
})(); })();

View File

@ -1,34 +1,34 @@
(function () { (function () {
var ns = $.namespace("pskl.selection"); var ns = $.namespace("pskl.selection");
ns.BaseSelection = function () { ns.BaseSelection = function () {
this.reset(); this.reset();
}; };
ns.BaseSelection.prototype.reset = function () { ns.BaseSelection.prototype.reset = function () {
this.pixels = []; this.pixels = [];
this.hasPastedContent = false; this.hasPastedContent = false;
}; };
ns.BaseSelection.prototype.move = function (colDiff, rowDiff) { ns.BaseSelection.prototype.move = function (colDiff, rowDiff) {
var movedPixel, movedPixels = []; var movedPixel, movedPixels = [];
for(var i=0, l=this.pixels.length; i<l; i++) { for(var i=0, l=this.pixels.length; i<l; i++) {
movedPixel = this.pixels[i]; movedPixel = this.pixels[i];
movedPixel.col += colDiff; movedPixel.col += colDiff;
movedPixel.row += rowDiff; movedPixel.row += rowDiff;
movedPixels.push(movedPixel); movedPixels.push(movedPixel);
} }
this.pixels = movedPixels; this.pixels = movedPixels;
}; };
ns.BaseSelection.prototype.fillSelectionFromFrame = function (targetFrame) { ns.BaseSelection.prototype.fillSelectionFromFrame = function (targetFrame) {
var pixelWithCopiedColor; var pixelWithCopiedColor;
for(var i=0, l=this.pixels.length; i<l; i++) { for(var i=0, l=this.pixels.length; i<l; i++) {
pixelWithCopiedColor = this.pixels[i]; pixelWithCopiedColor = this.pixels[i];
pixelWithCopiedColor.copiedColor = pixelWithCopiedColor.copiedColor =
targetFrame.getPixel(pixelWithCopiedColor.col, pixelWithCopiedColor.row); targetFrame.getPixel(pixelWithCopiedColor.col, pixelWithCopiedColor.row);
} }
this.hasPastedContent = true; this.hasPastedContent = true;
}; };
})(); })();

View File

@ -1,9 +1,9 @@
(function () { (function () {
var ns = $.namespace("pskl.selection"); var ns = $.namespace("pskl.selection");
ns.RectangularSelection = function (x0, y0, x1, y1) { ns.RectangularSelection = function (x0, y0, x1, y1) {
this.pixels = pskl.PixelUtils.getRectanglePixels(x0, y0, x1, y1); this.pixels = pskl.PixelUtils.getRectanglePixels(x0, y0, x1, y1);
}; };
pskl.utils.inherit(ns.RectangularSelection, ns.BaseSelection); pskl.utils.inherit(ns.RectangularSelection, ns.BaseSelection);
})(); })();

View File

@ -1,130 +1,130 @@
(function () { (function () {
var ns = $.namespace("pskl.selection"); var ns = $.namespace("pskl.selection");
ns.SelectionManager = function (framesheet, overlayFrame) { ns.SelectionManager = function (framesheet, overlayFrame) {
this.framesheet = framesheet; this.framesheet = framesheet;
this.overlayFrame = overlayFrame; this.overlayFrame = overlayFrame;
this.currentSelection = null; this.currentSelection = null;
$.subscribe(Events.SELECTION_CREATED, $.proxy(this.onSelectionCreated_, this)); $.subscribe(Events.SELECTION_CREATED, $.proxy(this.onSelectionCreated_, this));
$.subscribe(Events.SELECTION_DISMISSED, $.proxy(this.onSelectionDismissed_, this)); $.subscribe(Events.SELECTION_DISMISSED, $.proxy(this.onSelectionDismissed_, this));
$.subscribe(Events.SELECTION_MOVE_REQUEST, $.proxy(this.onSelectionMoved_, this)); $.subscribe(Events.SELECTION_MOVE_REQUEST, $.proxy(this.onSelectionMoved_, this));
$.subscribe(Events.PASTE, $.proxy(this.onPaste_, this)); $.subscribe(Events.PASTE, $.proxy(this.onPaste_, this));
$.subscribe(Events.COPY, $.proxy(this.onCopy_, this)); $.subscribe(Events.COPY, $.proxy(this.onCopy_, this));
$.subscribe(Events.CUT, $.proxy(this.onCut_, this)); $.subscribe(Events.CUT, $.proxy(this.onCut_, this));
$.subscribe(Events.TOOL_SELECTED, $.proxy(this.onToolSelected_, this)); $.subscribe(Events.TOOL_SELECTED, $.proxy(this.onToolSelected_, this));
}; };
/** /**
* @private * @private
*/ */
ns.SelectionManager.prototype.cleanSelection_ = function(selection) { ns.SelectionManager.prototype.cleanSelection_ = function(selection) {
if(this.currentSelection) { if(this.currentSelection) {
this.currentSelection.reset(); this.currentSelection.reset();
} }
this.overlayFrame.clear(); this.overlayFrame.clear();
}; };
/** /**
* @private * @private
*/ */
ns.SelectionManager.prototype.onToolSelected_ = function(evt, tool) { ns.SelectionManager.prototype.onToolSelected_ = function(evt, tool) {
var isSelectionTool = tool instanceof pskl.drawingtools.BaseSelect; var isSelectionTool = tool instanceof pskl.drawingtools.BaseSelect;
if(!isSelectionTool) { if(!isSelectionTool) {
this.cleanSelection_(); this.cleanSelection_();
} }
}; };
/** /**
* @private * @private
*/ */
ns.SelectionManager.prototype.onSelectionDismissed_ = function(evt) { ns.SelectionManager.prototype.onSelectionDismissed_ = function(evt) {
this.cleanSelection_(); this.cleanSelection_();
}; };
/** /**
* @private * @private
*/ */
ns.SelectionManager.prototype.onCut_ = function(evt) { ns.SelectionManager.prototype.onCut_ = function(evt) {
if(this.currentSelection) { if(this.currentSelection) {
// Put cut target into the selection: // Put cut target into the selection:
this.currentSelection.fillSelectionFromFrame(this.framesheet.getCurrentFrame()); this.currentSelection.fillSelectionFromFrame(this.framesheet.getCurrentFrame());
var pixels = this.currentSelection.pixels; var pixels = this.currentSelection.pixels;
var currentFrame = this.framesheet.getCurrentFrame(); var currentFrame = this.framesheet.getCurrentFrame();
for(var i=0, l=pixels.length; i<l; i++) { for(var i=0, l=pixels.length; i<l; i++) {
try { try {
currentFrame.setPixel(pixels[i].col, pixels[i].row, Constants.TRANSPARENT_COLOR); currentFrame.setPixel(pixels[i].col, pixels[i].row, Constants.TRANSPARENT_COLOR);
} }
catch(e) { catch(e) {
// Catchng out of frame's bound pixels without testing // Catchng out of frame's bound pixels without testing
} }
} }
} }
else { else {
throw "Bad state for CUT callback in SelectionManager"; throw "Bad state for CUT callback in SelectionManager";
} }
}; };
ns.SelectionManager.prototype.onPaste_ = function(evt) { ns.SelectionManager.prototype.onPaste_ = function(evt) {
if(this.currentSelection && this.currentSelection.hasPastedContent) { if(this.currentSelection && this.currentSelection.hasPastedContent) {
var pixels = this.currentSelection.pixels; var pixels = this.currentSelection.pixels;
var currentFrame = this.framesheet.getCurrentFrame(); var currentFrame = this.framesheet.getCurrentFrame();
for(var i=0, l=pixels.length; i<l; i++) { for(var i=0, l=pixels.length; i<l; i++) {
try { try {
currentFrame.setPixel( currentFrame.setPixel(
pixels[i].col, pixels[i].row, pixels[i].col, pixels[i].row,
pixels[i].copiedColor); pixels[i].copiedColor);
} }
catch(e) { catch(e) {
// Catchng out of frame's bound pixels without testing // Catchng out of frame's bound pixels without testing
} }
} }
} }
}; };
/** /**
* @private * @private
*/ */
ns.SelectionManager.prototype.onCopy_ = function(evt) { ns.SelectionManager.prototype.onCopy_ = function(evt) {
if(this.currentSelection && this.framesheet.getCurrentFrame()) { if(this.currentSelection && this.framesheet.getCurrentFrame()) {
this.currentSelection.fillSelectionFromFrame(this.framesheet.getCurrentFrame()); this.currentSelection.fillSelectionFromFrame(this.framesheet.getCurrentFrame());
} }
else { else {
throw "Bad state for CUT callback in SelectionManager"; throw "Bad state for CUT callback in SelectionManager";
} }
}; };
/** /**
* @private * @private
*/ */
ns.SelectionManager.prototype.onSelectionCreated_ = function(evt, selection) { ns.SelectionManager.prototype.onSelectionCreated_ = function(evt, selection) {
if(selection) { if(selection) {
this.currentSelection = selection; this.currentSelection = selection;
var pixels = selection.pixels; var pixels = selection.pixels;
for(var i=0, l=pixels.length; i<l; i++) { for(var i=0, l=pixels.length; i<l; i++) {
this.overlayFrame.setPixel(pixels[i].col, pixels[i].row, Constants.SELECTION_TRANSPARENT_COLOR); this.overlayFrame.setPixel(pixels[i].col, pixels[i].row, Constants.SELECTION_TRANSPARENT_COLOR);
} }
} }
else { else {
throw "No selection set in SelectionManager"; throw "No selection set in SelectionManager";
} }
}; };
/** /**
* @private * @private
*/ */
ns.SelectionManager.prototype.onSelectionMoved_ = function(evt, colDiff, rowDiff) { ns.SelectionManager.prototype.onSelectionMoved_ = function(evt, colDiff, rowDiff) {
if(this.currentSelection) { if(this.currentSelection) {
this.currentSelection.move(colDiff, rowDiff); this.currentSelection.move(colDiff, rowDiff);
} }
else { else {
throw "Bad state: No currentSelection set when trying to move it in SelectionManager"; throw "Bad state: No currentSelection set when trying to move it in SelectionManager";
} }
}; };
})(); })();

View File

@ -1,9 +1,9 @@
(function () { (function () {
var ns = $.namespace("pskl.selection"); var ns = $.namespace("pskl.selection");
ns.ShapeSelection = function (pixels) { ns.ShapeSelection = function (pixels) {
this.pixels = pixels; this.pixels = pixels;
}; };
pskl.utils.inherit(ns.ShapeSelection, ns.BaseSelection); pskl.utils.inherit(ns.ShapeSelection, ns.BaseSelection);
})(); })();

View File

@ -1,15 +1,15 @@
(function () { (function () {
var ns = $.namespace("pskl.service"); var ns = $.namespace("pskl.service");
ns.HistoryService = function (framesheet) { ns.HistoryService = function (framesheet) {
this.framesheet = framesheet; this.framesheet = framesheet;
}; };
ns.HistoryService.prototype.init = function () { ns.HistoryService.prototype.init = function () {
$.subscribe(Events.TOOL_RELEASED, this.saveState.bind(this)); $.subscribe(Events.TOOL_RELEASED, this.saveState.bind(this));
$.subscribe(Events.UNDO, this.undo.bind(this)); $.subscribe(Events.UNDO, this.undo.bind(this));
$.subscribe(Events.REDO, this.redo.bind(this)); $.subscribe(Events.REDO, this.redo.bind(this));
}; };
ns.HistoryService.prototype.saveState = function () { ns.HistoryService.prototype.saveState = function () {
this.framesheet.getCurrentFrame().saveState(); this.framesheet.getCurrentFrame().saveState();

View File

@ -1,64 +1,64 @@
(function () { (function () {
var ns = $.namespace("pskl.service"); var ns = $.namespace("pskl.service");
ns.KeyboardEventService = function () {}; ns.KeyboardEventService = function () {};
/** /**
* @private * @private
*/ */
ns.KeyboardEventService.prototype.KeyboardActions_ = { ns.KeyboardEventService.prototype.KeyboardActions_ = {
"ctrl" : { "ctrl" : {
"z" : Events.UNDO, "z" : Events.UNDO,
"y" : Events.REDO, "y" : Events.REDO,
"x" : Events.CUT, "x" : Events.CUT,
"c" : Events.COPY, "c" : Events.COPY,
"v" : Events.PASTE "v" : Events.PASTE
} }
}; };
/** /**
* @private * @private
*/ */
ns.KeyboardEventService.prototype.CharCodeToKeyCodeMap_ = { ns.KeyboardEventService.prototype.CharCodeToKeyCodeMap_ = {
90 : "z", 90 : "z",
89 : "y", 89 : "y",
88 : "x", 88 : "x",
67 : "c", 67 : "c",
86 : "v" 86 : "v"
}; };
/** /**
* @private * @private
*/ */
ns.KeyboardEventService.prototype.onKeyUp_ = function(evt) { ns.KeyboardEventService.prototype.onKeyUp_ = function(evt) {
var isMac = false; var isMac = false;
if (navigator.appVersion.indexOf("Mac")!=-1) { if (navigator.appVersion.indexOf("Mac")!=-1) {
// Welcome in mac world where vowels are consons and meta used instead of ctrl: // Welcome in mac world where vowels are consons and meta used instead of ctrl:
isMac = true; isMac = true;
} }
if (isMac ? evt.metaKey : evt.ctrlKey) { if (isMac ? evt.metaKey : evt.ctrlKey) {
// Get key pressed: // Get key pressed:
var letter = this.CharCodeToKeyCodeMap_[evt.which]; var letter = this.CharCodeToKeyCodeMap_[evt.which];
if(letter) { if(letter) {
var eventToTrigger = this.KeyboardActions_.ctrl[letter]; var eventToTrigger = this.KeyboardActions_.ctrl[letter];
if(eventToTrigger) { if(eventToTrigger) {
$.publish(eventToTrigger); $.publish(eventToTrigger);
evt.preventDefault(); evt.preventDefault();
return false; return false;
} }
} }
} }
}; };
/** /**
* @public * @public
*/ */
ns.KeyboardEventService.prototype.init = function() { ns.KeyboardEventService.prototype.init = function() {
$(document.body).keydown($.proxy(this.onKeyUp_, this)); $(document.body).keydown($.proxy(this.onKeyUp_, this));
}; };
})(); })();

View File

@ -4,7 +4,7 @@
ns.LocalStorageService = function (framesheet_) { ns.LocalStorageService = function (framesheet_) {
if(framesheet_ === undefined) { if(framesheet_ === undefined) {
throw "Bad LocalStorageService initialization: <undefined frameSheet>"; throw "Bad LocalStorageService initialization: <undefined frameSheet>";
} }
this.framesheet = framesheet_; this.framesheet = framesheet_;
this.localStorageThrottler_ = null; this.localStorageThrottler_ = null;

View File

@ -1,22 +1,22 @@
(function () { (function () {
var ns = $.namespace("pskl"); var ns = $.namespace("pskl");
ns.CanvasUtils = { ns.CanvasUtils = {
createCanvas : function (width, height, classList) { createCanvas : function (width, height, classList) {
var canvas = document.createElement("canvas"); var canvas = document.createElement("canvas");
canvas.setAttribute("width", width); canvas.setAttribute("width", width);
canvas.setAttribute("height", height); canvas.setAttribute("height", height);
if (typeof classList == "string") { if (typeof classList == "string") {
classList = [classList]; classList = [classList];
} }
if (Array.isArray(classList)) { if (Array.isArray(classList)) {
for (var i = 0 ; i < classList.length ; i++) { for (var i = 0 ; i < classList.length ; i++) {
canvas.classList.add(classList[i]); canvas.classList.add(classList[i]);
} }
} }
return canvas; return canvas;
} }
}; };
})(); })();

View File

@ -1,175 +1,177 @@
(function () { (function () {
var ns = $.namespace("pskl"); var ns = $.namespace("pskl");
ns.PixelUtils = { ns.PixelUtils = {
getRectanglePixels : function (x0, y0, x1, y1) { getRectanglePixels : function (x0, y0, x1, y1) {
var rectangle = this.getOrderedRectangleCoordinates(x0, y0, x1, y1); var rectangle = this.getOrderedRectangleCoordinates(x0, y0, x1, y1);
var pixels = []; var pixels = [];
for(var x = rectangle.x0; x <= rectangle.x1; x++) { for(var x = rectangle.x0; x <= rectangle.x1; x++) {
for(var y = rectangle.y0; y <= rectangle.y1; y++) { for(var y = rectangle.y0; y <= rectangle.y1; y++) {
pixels.push({"col": x, "row": 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 });
} }
} catch(e) {
return pixels; // Frame out of bound exception.
}, }
}
getBoundRectanglePixels : function (x0, y0, x1, y1) { // Security loop breaker:
var rectangle = this.getOrderedRectangleCoordinates(x0, y0, x1, y1); if(loopCount > 10 * cellCount) {
var pixels = []; console.log("loop breaker called");
// Creating horizontal sides of the rectangle: break;
for(var x = rectangle.x0; x <= rectangle.x1; x++) { }
pixels.push({"col": x, "row": rectangle.y0}); }
pixels.push({"col": x, "row": rectangle.y1}); return paintedPixels;
} },
// Creating vertical sides of the rectangle: /**
for(var y = rectangle.y0; y <= rectangle.y1; y++) { * Calculate and return the maximal DPI to display a picture in a given container.
pixels.push({"col": rectangle.x0, "row": y}); *
pixels.push({"col": rectangle.x1, "row": y}); * @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 pixels; * @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. * Calculate and return the maximal DPI to display a picture for a given height and width.
* In returned object {x0, y0} => top left corner - {x1, y1} => bottom right corner *
* @private * @param height number Height available to display the picture
*/ * @param width number Width available to display the picture
getOrderedRectangleCoordinates : function (x0, y0, x1, y1) { * @param number pictureHeight height in pixels of the picture to display
return { * @param number pictureWidth width in pixels of the picture to display
x0 : Math.min(x0, x1), y0 : Math.min(y0, y1), * @return number maximal dpi
x1 : Math.max(x0, x1), y1 : Math.max(y0, y1), */
}; calculateDPI : function (height, width, pictureHeight, pictureWidth) {
}, var heightBoundDpi = Math.floor(height / pictureHeight),
widthBoundDpi = Math.floor(width / pictureWidth);
/** return Math.min(heightBoundDpi, widthBoundDpi);
* 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);
},
};
})(); })();

View File

@ -1,76 +1,76 @@
(function () { (function () {
var ns = $.namespace("pskl"); var ns = $.namespace("pskl");
ns.UserSettings = { ns.UserSettings = {
SHOW_GRID : 'SHOW_GRID', SHOW_GRID : 'SHOW_GRID',
CANVAS_BACKGROUND : 'CANVAS_BACKGROUND', CANVAS_BACKGROUND : 'CANVAS_BACKGROUND',
KEY_TO_DEFAULT_VALUE_MAP_ : { KEY_TO_DEFAULT_VALUE_MAP_ : {
'SHOW_GRID' : false, 'SHOW_GRID' : false,
'CANVAS_BACKGROUND' : 'medium-canvas-background' 'CANVAS_BACKGROUND' : 'medium-canvas-background'
}, },
/** /**
* @private * @private
*/ */
cache_ : {}, cache_ : {},
/** /**
* Static method to access a user defined settings value ot its default * Static method to access a user defined settings value ot its default
* value if not defined yet. * value if not defined yet.
*/ */
get : function (key) { get : function (key) {
this.checkKeyValidity_(key); this.checkKeyValidity_(key);
if (!(key in this.cache_)) { if (!(key in this.cache_)) {
this.cache_[key] = this.cache_[key] =
this.readFromLocalStorage_(key) || this.readFromDefaults_(key); this.readFromLocalStorage_(key) || this.readFromDefaults_(key);
} }
return this.cache_[key]; return this.cache_[key];
}, },
set : function (key, value) { set : function (key, value) {
this.checkKeyValidity_(key); this.checkKeyValidity_(key);
this.cache_[key] = value; this.cache_[key] = value;
this.writeToLocalStorage_(key, value); this.writeToLocalStorage_(key, value);
$.publish(Events.USER_SETTINGS_CHANGED, [key, value]); $.publish(Events.USER_SETTINGS_CHANGED, [key, value]);
}, },
/** /**
* @private * @private
*/ */
readFromLocalStorage_ : function(key) { readFromLocalStorage_ : function(key) {
var value = window.localStorage[key]; var value = window.localStorage[key];
if (typeof value != "undefined") { if (typeof value != "undefined") {
value = JSON.parse(value); value = JSON.parse(value);
} }
return value; return value;
}, },
/** /**
* @private * @private
*/ */
writeToLocalStorage_ : function(key, value) { writeToLocalStorage_ : function(key, value) {
// TODO(grosbouddha): Catch storage exception here. // TODO(grosbouddha): Catch storage exception here.
window.localStorage[key] = JSON.stringify(value); window.localStorage[key] = JSON.stringify(value);
}, },
/** /**
* @private * @private
*/ */
readFromDefaults_ : function (key) { readFromDefaults_ : function (key) {
return this.KEY_TO_DEFAULT_VALUE_MAP_[key]; return this.KEY_TO_DEFAULT_VALUE_MAP_[key];
}, },
/** /**
* @private * @private
*/ */
checkKeyValidity_ : function(key) { checkKeyValidity_ : function(key) {
if(!(key in this.KEY_TO_DEFAULT_VALUE_MAP_)) { if(!(key in this.KEY_TO_DEFAULT_VALUE_MAP_)) {
// TODO(grosbouddha): Define error catching strategy and throw exception from here. // TODO(grosbouddha): Define error catching strategy and throw exception from here.
console.log("UserSettings key <"+ key +"> not find in supported keys."); console.log("UserSettings key <"+ key +"> not find in supported keys.");
} }
} }
}; };
})(); })();

View File

@ -1,14 +1,14 @@
jQuery.namespace = function() { jQuery.namespace = function() {
var a=arguments, o=null, i, j, d; var a=arguments, o=null, i, j, d;
for (i=0; i<a.length; i=i+1) { for (i=0; i<a.length; i=i+1) {
d=a[i].split("."); d=a[i].split(".");
o=window; o=window;
for (j=0; j<d.length; j=j+1) { for (j=0; j<d.length; j=j+1) {
o[d[j]]=o[d[j]] || {}; o[d[j]]=o[d[j]] || {};
o=o[d[j]]; o=o[d[j]];
}
} }
return o; }
return o;
}; };
/* /*
@ -18,19 +18,19 @@ jQuery.namespace = function() {
*/ */
(function() { // namespace: pskl.utils (function() { // namespace: pskl.utils
var ns = $.namespace("pskl.utils"); var ns = $.namespace("pskl.utils");
ns.rgbToHex = function(r, g, b) { ns.rgbToHex = function(r, g, b) {
if (r > 255 || g > 255 || b > 255) if (r > 255 || g > 255 || b > 255)
throw "Invalid color component"; throw "Invalid color component";
return ((r << 16) | (g << 8) | b).toString(16); return ((r << 16) | (g << 8) | b).toString(16);
}; };
ns.inherit = function(extendedObject, inheritFrom) { ns.inherit = function(extendedObject, inheritFrom) {
extendedObject.prototype = Object.create(inheritFrom.prototype); extendedObject.prototype = Object.create(inheritFrom.prototype);
extendedObject.prototype.constructor = extendedObject; extendedObject.prototype.constructor = extendedObject;
extendedObject.prototype.superclass = inheritFrom.prototype; extendedObject.prototype.superclass = inheritFrom.prototype;
}; };
})(); })();

View File

@ -15,6 +15,7 @@
"grunt": "~0.4.1", "grunt": "~0.4.1",
"grunt-contrib-connect": "0.3.0", "grunt-contrib-connect": "0.3.0",
"grunt-contrib-jshint": "0.5.4", "grunt-contrib-jshint": "0.5.4",
"grunt-ghost": "1.0.12" "grunt-ghost": "1.0.12",
"grunt-leading-indent" : "0.1.0"
} }
} }