From d4052b3609c599dbffc6156544acc9ffdb9690f6 Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Mon, 25 Apr 2016 22:30:10 -0400 Subject: [PATCH 001/177] put in a simple glyph class. start cleaning up existing bitmap --- src/font/bitmapfont.js | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/src/font/bitmapfont.js b/src/font/bitmapfont.js index 155ddf1487..3e8d5f9a6f 100644 --- a/src/font/bitmapfont.js +++ b/src/font/bitmapfont.js @@ -23,7 +23,7 @@ * @param {Number} [scale=1.0] * @param {Number} [firstChar=0x20] charcode for the first character in the font sheet. Default is the space character. */ - me.BitmapFont = me.Font.extend( + me.BitmapFont = me.Renderable.extend( /** @scope me.BitmapFont.prototype */ { /** @ignore */ init : function (font, size, scale, firstChar) { @@ -34,13 +34,10 @@ // #char per row this.charCount = 0; // font name and type - this._super(me.Font, "init", [font, size.x || size, "#000000"]); + this._super(me.Renderable, "init", [0, 0, 0, 0, 0, 0]); // first char in the ascii table this.firstChar = firstChar || 0x20; - // load the font metrics - this.loadFontMetrics(font, size); - // set a default alignement this.textAlign = "left"; this.textBaseline = "top"; @@ -50,23 +47,6 @@ } }, - /** - * Load the font metrics - * @ignore - */ - loadFontMetrics : function (font, size) { - this.font = me.loader.getImage(font); - - // some cheap metrics - this.fontSize.x = size.x || size; - this.fontSize.y = size.y || this.font.height; - this.sSize.copy(this.fontSize); - this.height = this.sSize.y; - - // #char per row - this.charCount = ~~(this.font.width / this.fontSize.x); - }, - /** * change the font settings * @name set @@ -196,4 +176,21 @@ renderer.setGlobalAlpha(_alpha); } }); + + me.Glyph = me.Object.extend({ + init: function() { + this.id = 0; + this.src = new me.Vector2d(); + this.width = 0; + this.height = 0; + this.u = 0; + this.v = 0; + this.u2 = 0; + this.v2 = 0; + this.offset = new me.Vector2d(); + this.xadvance = 0; + this.kerning = []; + this.fixedWidth = false; + } + }); })(); From 43e871a4c8856abc78133793a5075e731cf5fa4e Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Mon, 25 Apr 2016 23:21:26 -0400 Subject: [PATCH 002/177] loader setup. Also renamed regex constants to be capitalized --- src/font/bitmapfont.js | 25 ++++++++++++++++++++++++- src/utils/utils.js | 6 +++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/font/bitmapfont.js b/src/font/bitmapfont.js index 3e8d5f9a6f..fda481a0a0 100644 --- a/src/font/bitmapfont.js +++ b/src/font/bitmapfont.js @@ -14,6 +14,11 @@ (function () { /** * a bitpmap font object + * Use me.loader.preload or me.loader.load to load assets + * me.loader.preload([ + * { name: "arial", type: "binary" src: "data/font/arial.fnt" }, + * { name: "arial", type: "image" src: "data/font/arial.png" }, + * ]) * @class * @extends me.Font * @memberOf me @@ -26,11 +31,22 @@ me.BitmapFont = me.Renderable.extend( /** @scope me.BitmapFont.prototype */ { /** @ignore */ - init : function (font, size, scale, firstChar) { + init : function (fontName, size, scale, firstChar) { /** @ignore */ // scaled font size; this.sSize = new me.Vector2d(); + this.fontData = me.loader.getBinary(fontName); + this.fontImage = me.loader.getImage(fontName); + + if (!this.fontData) { + throw "Font data for font name: " + fontName + " not found"; + } + + if (!this.fontImage) { + throw "Font image for font name: " + fontName + " not found"; + } + // #char per row this.charCount = 0; // font name and type @@ -177,6 +193,13 @@ } }); + /** + * a glyph representing a single character in a font + * @class + * @extends me.Glyph + * @memberOf me + * @constructor + */ me.Glyph = me.Object.extend({ init: function() { this.id = 0; diff --git a/src/utils/utils.js b/src/utils/utils.js index 1e75af60f3..4091e5fdfb 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -121,8 +121,8 @@ var GUID_index = 0; // regexp to deal with file name & path - var removepath = /^.*(\\|\/|\:)/; - var removeext = /\.[^\.]*$/; + var REMOVE_PATH = /^.*(\\|\/|\:)/; + var REMOVE_EXT = /\.[^\.]*$/; /* * PUBLIC STUFF @@ -222,7 +222,7 @@ * @return {String} the base name without path information. */ api.getBasename = function (path) { - return path.replace(removepath, "").replace(removeext, ""); + return path.replace(REMOVE_PATH, "").replace(REMOVE_EXT, ""); }; /** From 690d293a695c0b84756b6f7c4297f8972e2c1ce3 Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Wed, 27 Apr 2016 23:36:44 -0400 Subject: [PATCH 003/177] setup a basic class to store the data, based on libgdx implementation --- src/font/bitmapfont.js | 51 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/font/bitmapfont.js b/src/font/bitmapfont.js index fda481a0a0..7d105a7164 100644 --- a/src/font/bitmapfont.js +++ b/src/font/bitmapfont.js @@ -216,4 +216,55 @@ this.fixedWidth = false; } }); + + /** + * Class for storing relevant data from the font file. + * @private + * @class me.BitmapFontData + * @memberOf me + * @constructor + */ + me.BitmapFontData = me.Object.extend({ + init: function (flipped) { + this.flipped = flipped; + this.padTop = 0; + this.padRight = 0; + this.padBottom = 0; + this.padLeft = 0; + // The distance from one line of text to the next. To set this value, use {@link #setLineHeight(float)}. + this.lineHeight = 0; + // The distance from the top of most uppercase characters to the baseline. Since the drawing position is the cap height of + // the first line, the cap height can be used to get the location of the baseline. + this.capHeight = 1; + // The distance from the cap height to the top of the tallest glyph. + this.ascent = 0; + // The distance from the bottom of the glyph that extends the lowest to the baseline. This number is negative. + this.descent = 0; + this.down = 0; + this.scale = new me.Vector2d(); + // The amount to add to the glyph X position when drawing a cursor between glyphs. This field is not set by the BMFont + // file, it needs to be set manually depending on how the glyphs are rendered on the backing textures. + this.cursorX = 0; + + this.glyphs = []; + // The glyph to display for characters not in the font. May be null. + this.missingGlyph = null; + + // The width of the space character. + this.spaceWidth = 0; + // The x-height, which is the distance from the top of most lowercase characters to the baseline. + this.xHeight = 1; + + this.xChars = ['x', 'e', 'a', 'o', 'n', 's', 'r', 'c', 'u', 'm', 'v', 'w', 'z']; + this.capChars = ['M', 'N', 'B', 'D', 'C', 'E', 'F', 'K', 'A', 'G', 'H', 'I', 'J', 'L', 'O', 'P', 'Q', 'R', 'S', + 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']; + }, + + parse: function (fontData) { + var lines = fontData.split(/\r\n|\n/); + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + } + } + }); })(); From 2eb3a06530c5413273d17fcd04124a3ea998b0ee Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Wed, 27 Apr 2016 23:58:33 -0400 Subject: [PATCH 004/177] starting on parsing --- src/font/bitmapfont.js | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/font/bitmapfont.js b/src/font/bitmapfont.js index 7d105a7164..e2db5a2cc9 100644 --- a/src/font/bitmapfont.js +++ b/src/font/bitmapfont.js @@ -260,10 +260,44 @@ 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']; }, + _getValueFromPair: function (string, pattern) { + var value = string.match(pattern); + if (!value) { + throw "Could not find pattern " + pattern + " in string: " + string; + } + + return value[0].split('=')[1]; + }, + parse: function (fontData) { + if (!fontData) { + throw "File containing font data was empty, cannot load the bitmap font."; + } var lines = fontData.split(/\r\n|\n/); - for (var i = 0; i < lines.length; i++) { + var padding = fontData.match(/padding\=\d+,\d+,\d+,\d+/g); + if (!padding) { + throw "Padding not found in first line"; + } + var paddingValues = padding.split('=')[1].split(','); + this.padTop = parseFloat(paddingValues[0]); + this.padLeft = parseFloat(paddingValues[1]); + this.padBottom = parseFloat(paddingValues[2]); + this.padRight = parseFloat(paddingValues[3]); + + this.lineHeight = parseFloat(this._getValueFromPair(lines[1], /lineHeight\=\d+/g)); + + var baseLine = parseFloat(this._getValueFromPair(lines[1], /base\=\d+/g)); + + + for (var i = 4; i < lines.length; i++) { var line = lines[i]; + if (/^kernings/.test(line)) { + continue; + } + + var glyph = new me.Glyph(); + + var characterValues = line.split('='); } } }); From ad3bde12e2544a44a99ad4f18b3112e5aee05f22 Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Thu, 28 Apr 2016 21:55:58 -0400 Subject: [PATCH 005/177] set char values on glyph --- src/font/bitmapfont.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/font/bitmapfont.js b/src/font/bitmapfont.js index e2db5a2cc9..6fbdd70d48 100644 --- a/src/font/bitmapfont.js +++ b/src/font/bitmapfont.js @@ -288,7 +288,6 @@ var baseLine = parseFloat(this._getValueFromPair(lines[1], /base\=\d+/g)); - for (var i = 4; i < lines.length; i++) { var line = lines[i]; if (/^kernings/.test(line)) { @@ -298,7 +297,25 @@ var glyph = new me.Glyph(); var characterValues = line.split('='); + + glyph.id = parseFloat(characterValues[2]); + glyph.src.set(parseFloat(characterValues[4]), parseFloat(characterValues[6])); + glyph.width = parseFloat(characterValues[8]); + glyph.height = parseFloat(characterValues[10]); + var y = parseFloat(characterValues[14]); + if (this.flipped) { + y = -(glyph.height + parseFloat(characterValues[14])); + } + glyph.offset.set(parseFloat(characterValues[12]), y); + + glyph.xadvance = parseFloat(characterValues[16]); + + if (glyph.width > 0 && glyph.height > 0) { + this.descent = Math.min(baseLine + glyph.yoffset, this.descent); + } } + + this.descent += this.padBottom; } }); })(); From 50423afc10b4f39775ccb542a0973d208fecc2f1 Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Thu, 28 Apr 2016 22:03:09 -0400 Subject: [PATCH 006/177] fix string quotes, remove old table. --- src/font/bitmapfont.js | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/font/bitmapfont.js b/src/font/bitmapfont.js index 6fbdd70d48..5cd239ec58 100644 --- a/src/font/bitmapfont.js +++ b/src/font/bitmapfont.js @@ -5,10 +5,6 @@ * * Font / Bitmap font * - * ASCII Table - * http://www.asciitable.com/ - * [ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz] - * * -> first char " " 32d (0x20); */ (function () { @@ -255,9 +251,9 @@ // The x-height, which is the distance from the top of most lowercase characters to the baseline. this.xHeight = 1; - this.xChars = ['x', 'e', 'a', 'o', 'n', 's', 'r', 'c', 'u', 'm', 'v', 'w', 'z']; - this.capChars = ['M', 'N', 'B', 'D', 'C', 'E', 'F', 'K', 'A', 'G', 'H', 'I', 'J', 'L', 'O', 'P', 'Q', 'R', 'S', - 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']; + this.xChars = ["x", "e", "a", "o", "n", "s", "r", "c", "u", "m", "v", "w", "z"]; + this.capChars = ["M", "N", "B", "D", "C", "E", "F", "K", "A", "G", "H", "I", "J", "L", "O", "P", "Q", "R", "S", + "T", "U", "V", "W", "X", "Y", "Z"]; }, _getValueFromPair: function (string, pattern) { @@ -266,7 +262,7 @@ throw "Could not find pattern " + pattern + " in string: " + string; } - return value[0].split('=')[1]; + return value[0].split("=")[1]; }, parse: function (fontData) { @@ -278,7 +274,7 @@ if (!padding) { throw "Padding not found in first line"; } - var paddingValues = padding.split('=')[1].split(','); + var paddingValues = padding.split("=")[1].split(","); this.padTop = parseFloat(paddingValues[0]); this.padLeft = parseFloat(paddingValues[1]); this.padBottom = parseFloat(paddingValues[2]); @@ -296,7 +292,7 @@ var glyph = new me.Glyph(); - var characterValues = line.split('='); + var characterValues = line.split("="); glyph.id = parseFloat(characterValues[2]); glyph.src.set(parseFloat(characterValues[4]), parseFloat(characterValues[6])); From db2290ba164d01c2698ebd545080bf16a44ee857 Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Thu, 28 Apr 2016 22:06:13 -0400 Subject: [PATCH 007/177] use object pooling for the glyphs --- src/font/bitmapfont.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/font/bitmapfont.js b/src/font/bitmapfont.js index 5cd239ec58..987ba0f1d5 100644 --- a/src/font/bitmapfont.js +++ b/src/font/bitmapfont.js @@ -197,7 +197,11 @@ * @constructor */ me.Glyph = me.Object.extend({ - init: function() { + init: function () { + this.onResetEvent(); + }, + + onResetEvent: function () { this.id = 0; this.src = new me.Vector2d(); this.width = 0; @@ -290,7 +294,7 @@ continue; } - var glyph = new me.Glyph(); + var glyph = me.pool.pull("me.Glyph"); var characterValues = line.split("="); From f61c7fe115e973fef4fe5b32ea23595462dac3e1 Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Thu, 28 Apr 2016 22:45:17 -0400 Subject: [PATCH 008/177] setup kerning parsing --- src/font/bitmapfont.js | 139 ++++++++++++++++++++++++++--------------- 1 file changed, 88 insertions(+), 51 deletions(-) diff --git a/src/font/bitmapfont.js b/src/font/bitmapfont.js index 987ba0f1d5..db688fd7f0 100644 --- a/src/font/bitmapfont.js +++ b/src/font/bitmapfont.js @@ -8,6 +8,9 @@ * -> first char " " 32d (0x20); */ (function () { + var LOG2_PAGE_SIZE = 9; + var PAGE_SIZE = 1 << LOG2_PAGE_SIZE; + /** * a bitpmap font object * Use me.loader.preload or me.loader.load to load assets @@ -214,6 +217,14 @@ this.xadvance = 0; this.kerning = []; this.fixedWidth = false; + }, + + setKerning: function (ch, value) { + var page = this.kerning[ch >>> LOG2_PAGE_SIZE]; + if (page === null) { + this.kerning[ch >>> LOG2_PAGE_SIZE] = page = []; + } + page[ch & PAGE_SIZE - 1] = value; } }); @@ -246,7 +257,7 @@ // file, it needs to be set manually depending on how the glyphs are rendered on the backing textures. this.cursorX = 0; - this.glyphs = []; + this.glyphs = {}; // The glyph to display for characters not in the font. May be null. this.missingGlyph = null; @@ -258,64 +269,90 @@ this.xChars = ["x", "e", "a", "o", "n", "s", "r", "c", "u", "m", "v", "w", "z"]; this.capChars = ["M", "N", "B", "D", "C", "E", "F", "K", "A", "G", "H", "I", "J", "L", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]; - }, - - _getValueFromPair: function (string, pattern) { - var value = string.match(pattern); - if (!value) { - throw "Could not find pattern " + pattern + " in string: " + string; - } - - return value[0].split("=")[1]; - }, - - parse: function (fontData) { - if (!fontData) { - throw "File containing font data was empty, cannot load the bitmap font."; - } - var lines = fontData.split(/\r\n|\n/); - var padding = fontData.match(/padding\=\d+,\d+,\d+,\d+/g); - if (!padding) { - throw "Padding not found in first line"; - } - var paddingValues = padding.split("=")[1].split(","); - this.padTop = parseFloat(paddingValues[0]); - this.padLeft = parseFloat(paddingValues[1]); - this.padBottom = parseFloat(paddingValues[2]); - this.padRight = parseFloat(paddingValues[3]); + }, - this.lineHeight = parseFloat(this._getValueFromPair(lines[1], /lineHeight\=\d+/g)); + _createSpaceGlyph: function () { + var spaceCharCode = " ".charCodeAt(0); + if (!this.glyphs[spaceCharCode]) { + var glyph = me.pool.pull("me.Glyph"); + glyph.id = spaceCharCode; + glyph.xadvance = this.glyphs[Object.keys(this.glyphs)[0]].xadvance; + this.glyphs[spaceCharCode] = glyph; + + if (glyph.width === 0) { + glyph.width = ~~(this.padLeft + glyph.xadvance + this.padRight); + glyph.offset.set(-this.padLeft, 0); + } - var baseLine = parseFloat(this._getValueFromPair(lines[1], /base\=\d+/g)); + this.spaceWidth = glyph.width; + } + }, - for (var i = 4; i < lines.length; i++) { - var line = lines[i]; - if (/^kernings/.test(line)) { - continue; - } + _getValueFromPair: function (string, pattern) { + var value = string.match(pattern); + if (!value) { + throw "Could not find pattern " + pattern + " in string: " + string; + } - var glyph = me.pool.pull("me.Glyph"); + return value[0].split("=")[1]; + }, - var characterValues = line.split("="); + parse: function (fontData) { + if (!fontData) { + throw "File containing font data was empty, cannot load the bitmap font."; + } + var lines = fontData.split(/\r\n|\n/); + var padding = fontData.match(/padding\=\d+,\d+,\d+,\d+/g); + if (!padding) { + throw "Padding not found in first line"; + } + var paddingValues = padding.split("=")[1].split(","); + this.padTop = parseFloat(paddingValues[0]); + this.padLeft = parseFloat(paddingValues[1]); + this.padBottom = parseFloat(paddingValues[2]); + this.padRight = parseFloat(paddingValues[3]); + + this.lineHeight = parseFloat(this._getValueFromPair(lines[1], /lineHeight\=\d+/g)); + + var baseLine = parseFloat(this._getValueFromPair(lines[1], /base\=\d+/g)); + + for (var i = 4; i < lines.length; i++) { + var line = lines[i]; + var characterValues = line.split("="); + if (/^kernings/.test(line)) { + var first = parseFloat(characterValues[2]); + var second = parseFloat(characterValues[4]); + var amount = parseFloat(characterValues[6]); + + var glyph = this.glyphs[first]; + if (glyph !== null) { + glyph.setKerning(second, amount); + } + } else { + var glyph = me.pool.pull("me.Glyph"); + + var ch = parseFloat(characterValues[2]); + glyph.id = ch + glyph.src.set(parseFloat(characterValues[4]), parseFloat(characterValues[6])); + glyph.width = parseFloat(characterValues[8]); + glyph.height = parseFloat(characterValues[10]); + var y = parseFloat(characterValues[14]); + if (this.flipped) { + y = -(glyph.height + parseFloat(characterValues[14])); + } + glyph.offset.set(parseFloat(characterValues[12]), y); - glyph.id = parseFloat(characterValues[2]); - glyph.src.set(parseFloat(characterValues[4]), parseFloat(characterValues[6])); - glyph.width = parseFloat(characterValues[8]); - glyph.height = parseFloat(characterValues[10]); - var y = parseFloat(characterValues[14]); - if (this.flipped) { - y = -(glyph.height + parseFloat(characterValues[14])); - } - glyph.offset.set(parseFloat(characterValues[12]), y); + glyph.xadvance = parseFloat(characterValues[16]); - glyph.xadvance = parseFloat(characterValues[16]); + if (glyph.width > 0 && glyph.height > 0) { + this.descent = Math.min(baseLine + glyph.yoffset, this.descent); + } - if (glyph.width > 0 && glyph.height > 0) { - this.descent = Math.min(baseLine + glyph.yoffset, this.descent); - } - } + this.glyphs[ch] = glyph; + } + } - this.descent += this.padBottom; - } + this.descent += this.padBottom; + } }); })(); From f562e44c4474c3b35c2e973004eda3dd80760eaa Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Fri, 29 Apr 2016 15:39:42 -0400 Subject: [PATCH 009/177] initialize internal vectors only once. --- src/font/bitmapfont.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/font/bitmapfont.js b/src/font/bitmapfont.js index db688fd7f0..b14d4e5e00 100644 --- a/src/font/bitmapfont.js +++ b/src/font/bitmapfont.js @@ -201,19 +201,21 @@ */ me.Glyph = me.Object.extend({ init: function () { + this.src = new me.Vector2d(); + this.offset = new me.Vector2d(); this.onResetEvent(); }, onResetEvent: function () { this.id = 0; - this.src = new me.Vector2d(); + this.src.set(0, 0); this.width = 0; this.height = 0; this.u = 0; this.v = 0; this.u2 = 0; this.v2 = 0; - this.offset = new me.Vector2d(); + this.offset.set(0, 0); this.xadvance = 0; this.kerning = []; this.fixedWidth = false; From 930a4cb966980673158638b9357af5e0279573ac Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Fri, 29 Apr 2016 16:05:15 -0400 Subject: [PATCH 010/177] finished parse function for now, fixed up lint errors. --- src/font/bitmapfont.js | 62 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/src/font/bitmapfont.js b/src/font/bitmapfont.js index b14d4e5e00..2d1bc6274b 100644 --- a/src/font/bitmapfont.js +++ b/src/font/bitmapfont.js @@ -278,7 +278,7 @@ if (!this.glyphs[spaceCharCode]) { var glyph = me.pool.pull("me.Glyph"); glyph.id = spaceCharCode; - glyph.xadvance = this.glyphs[Object.keys(this.glyphs)[0]].xadvance; + glyph.xadvance = this._getFirstGlyph().xadvance; this.glyphs[spaceCharCode] = glyph; if (glyph.width === 0) { @@ -290,6 +290,10 @@ } }, + _getFirstGlyph: function () { + return this.glyphs[Object.keys(this.glyphs)[0]]; + }, + _getValueFromPair: function (string, pattern) { var value = string.match(pattern); if (!value) { @@ -318,6 +322,10 @@ var baseLine = parseFloat(this._getValueFromPair(lines[1], /base\=\d+/g)); + var padY = this.padTop + this.padBottom; + + var glyph = null; + for (var i = 4; i < lines.length; i++) { var line = lines[i]; var characterValues = line.split("="); @@ -326,15 +334,15 @@ var second = parseFloat(characterValues[4]); var amount = parseFloat(characterValues[6]); - var glyph = this.glyphs[first]; + glyph = this.glyphs[first]; if (glyph !== null) { glyph.setKerning(second, amount); } } else { - var glyph = me.pool.pull("me.Glyph"); + glyph = me.pool.pull("me.Glyph"); var ch = parseFloat(characterValues[2]); - glyph.id = ch + glyph.id = ch; glyph.src.set(parseFloat(characterValues[4]), parseFloat(characterValues[6])); glyph.width = parseFloat(characterValues[8]); glyph.height = parseFloat(characterValues[10]); @@ -355,6 +363,52 @@ } this.descent += this.padBottom; + + this._createSpaceGlyph(); + + var xGlyph = null; + for (i = 0; i < this.xChars.length; i++) { + var xChar = this.xChars[i]; + xGlyph = this.glyphs[xChar.charCodeAt(0)]; + if (xGlyph) { + break; + } + } + if (!xGlyph) { + xGlyph = this._getFirstGlyph(); + } + + var capGlyph = null; + for (i = 0; i < this.capChars.length; i++) { + var capChar = this.capChars[i]; + capGlyph = this.glyphs[capChar.charCodeAt(0)]; + if (capGlyph) { + break; + } + } + if (!capGlyph) { + for (var charCode in this.glyphs) { + if (this.glyphs.hasOwnProperty(charCode)) { + glyph = this.glyphs[charCode]; + if (glyph.height === 0 || glyph.width === 0) { + continue; + } + this.capHeight = Math.max(this.capHeight, glyph.height); + } + } + } else { + this.capHeight = capGlyph.height; + } + + this.capHeight -= padY; + + this.ascent = this.baseLine - this.capHeight; + this.down = -this.lineHeight; + + if (this.flipped) { + this.ascent = -this.ascent; + this.down = -this.down; + } } }); })(); From 09df387019ff3c9e17d63120a99ef8d728286570 Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Sat, 30 Apr 2016 16:39:08 -0400 Subject: [PATCH 011/177] Bitmap font loader errors fixed. --- examples/font_test/atascii_8px.png | Bin 674 -> 0 bytes examples/font_test/main.js | 10 ++- examples/font_test/xolo12.fnt | 100 +++++++++++++++++++++++++++ examples/font_test/xolo12.png | Bin 0 -> 9845 bytes src/font/bitmapfont.js | 105 ++++++----------------------- src/system/pooling.js | 1 + 6 files changed, 130 insertions(+), 86 deletions(-) delete mode 100644 examples/font_test/atascii_8px.png create mode 100644 examples/font_test/xolo12.fnt create mode 100644 examples/font_test/xolo12.png diff --git a/examples/font_test/atascii_8px.png b/examples/font_test/atascii_8px.png deleted file mode 100644 index 6f59b6772811d4b15a9beea3db2724ae75e4cee1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 674 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 b(k<{moUZ9yDN$RN00000NkvXXu0mjfUM~}q literal 0 HcmV?d00001 diff --git a/src/font/bitmapfont.js b/src/font/bitmapfont.js index 2d1bc6274b..f7c6bd7c21 100644 --- a/src/font/bitmapfont.js +++ b/src/font/bitmapfont.js @@ -23,22 +23,26 @@ * @memberOf me * @constructor * @param {String} font font name - * @param {Number|Object} size either a number value, or an object like { x : 16, y : 16 } * @param {Number} [scale=1.0] - * @param {Number} [firstChar=0x20] charcode for the first character in the font sheet. Default is the space character. + * @param {String} [textAlign=left] + * @param {String} [textBaseline=top] */ me.BitmapFont = me.Renderable.extend( /** @scope me.BitmapFont.prototype */ { /** @ignore */ - init : function (fontName, size, scale, firstChar) { + init : function (fontName, scale, textAlign, textBaseline) { /** @ignore */ // scaled font size; - this.sSize = new me.Vector2d(); + this.sSize = me.pool.pull("me.Vector2d", 0, 0); - this.fontData = me.loader.getBinary(fontName); + var fontData = me.loader.getBinary(fontName); this.fontImage = me.loader.getImage(fontName); - if (!this.fontData) { + this.bitmapFontData = new me.BitmapFontData(false); + this.bitmapFontData.parse(fontData); + this.fontScale = me.pool.pull("me.Vector2d", 1, 1); + + if (!fontData) { throw "Font data for font name: " + fontName + " not found"; } @@ -50,12 +54,10 @@ this.charCount = 0; // font name and type this._super(me.Renderable, "init", [0, 0, 0, 0, 0, 0]); - // first char in the ascii table - this.firstChar = firstChar || 0x20; // set a default alignement - this.textAlign = "left"; - this.textBaseline = "top"; + this.textAlign = textAlign || "left"; + this.textBaseline = textBaseline || "top"; // resize if necessary if (scale) { this.resize(scale); @@ -86,11 +88,7 @@ * @param {Number} scale ratio */ resize : function (scale) { - // updated scaled Size - this.sSize.setV(this.fontSize); - this.sSize.x *= scale; - this.sSize.y *= scale; - this.height = this.sSize.y; + this.fontScale.set(scale, scale); }, /** @@ -124,71 +122,7 @@ * @param {Number} x * @param {Number} y */ - draw : function (renderer, text, x, y) { - var strings = ("" + text).split("\n"); - var lX = x; - var height = this.sSize.y * this.lineHeight; - - // save the previous global alpha value - var _alpha = renderer.globalAlpha(); - renderer.setGlobalAlpha(_alpha * this.getOpacity()); - - // update initial position - this.pos.set(x, y, this.pos.z); // TODO : z ? - for (var i = 0; i < strings.length; i++) { - x = lX; - var string = strings[i].trimRight(); - // adjust x pos based on alignment value - var width = string.length * this.sSize.x; - switch (this.textAlign) { - case "right": - x -= width; - break; - - case "center": - x -= width * 0.5; - break; - - default : - break; - } - - // adjust y pos based on alignment value - switch (this.textBaseline) { - case "middle": - y -= height * 0.5; - break; - - case "ideographic": - case "alphabetic": - case "bottom": - y -= height; - break; - - default : - break; - } - - // draw the string - for (var c = 0, len = string.length; c < len; c++) { - // calculate the char index - var idx = string.charCodeAt(c) - this.firstChar; - if (idx >= 0) { - // draw it - renderer.drawImage(this.font, - this.fontSize.x * (idx % this.charCount), - this.fontSize.y * ~~(idx / this.charCount), - this.fontSize.x, this.fontSize.y, - ~~x, ~~y, - this.sSize.x, this.sSize.y); - } - x += this.sSize.x; - } - // increment line - y += height; - } - // restore the previous global alpha value - renderer.setGlobalAlpha(_alpha); + draw : function () { // renderer, text, x, y } }); @@ -312,7 +246,7 @@ if (!padding) { throw "Padding not found in first line"; } - var paddingValues = padding.split("=")[1].split(","); + var paddingValues = padding[0].split("=")[1].split(","); this.padTop = parseFloat(paddingValues[0]); this.padLeft = parseFloat(paddingValues[1]); this.padBottom = parseFloat(paddingValues[2]); @@ -328,14 +262,17 @@ for (var i = 4; i < lines.length; i++) { var line = lines[i]; - var characterValues = line.split("="); - if (/^kernings/.test(line)) { + var characterValues = line.split(/=|\s/); + if (!line || /^kernings/.test(line)) { + continue; + } + if (/^kerning\s/.test(line)) { var first = parseFloat(characterValues[2]); var second = parseFloat(characterValues[4]); var amount = parseFloat(characterValues[6]); glyph = this.glyphs[first]; - if (glyph !== null) { + if (glyph !== null && typeof glyph !== "undefined") { glyph.setKerning(second, amount); } } else { diff --git a/src/system/pooling.js b/src/system/pooling.js index 9e883f2dc2..9f466e3186 100644 --- a/src/system/pooling.js +++ b/src/system/pooling.js @@ -45,6 +45,7 @@ api.register("me.Particle", me.Particle, true); api.register("me.Sprite", me.Sprite); api.register("me.Vector2d", me.Vector2d, true); + api.register("me.Glyph", me.Glyph, true); }; /** From 252374058cf303c1d1422aa749aa5c59722f442e Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Mon, 16 May 2016 22:42:28 -0400 Subject: [PATCH 012/177] working towards cache setup --- sourceFiles.json | 1 + src/font/bitmapfont.js | 63 ++++++++++++++++++++++++++++++++++--- src/font/bitmapfontcache.js | 23 ++++++++++++++ 3 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 src/font/bitmapfontcache.js diff --git a/sourceFiles.json b/sourceFiles.json index 3fe968d519..73efc9e00d 100644 --- a/sourceFiles.json +++ b/sourceFiles.json @@ -41,6 +41,7 @@ "src/loader/loader.js", "src/font/font.js", "src/font/bitmapfont.js", + "src/font/bitmapfontcache.js", "src/audio/audio.js", "src/video/video.js", "src/video/renderer.js", diff --git a/src/font/bitmapfont.js b/src/font/bitmapfont.js index f7c6bd7c21..49bb0ffc3b 100644 --- a/src/font/bitmapfont.js +++ b/src/font/bitmapfont.js @@ -12,14 +12,14 @@ var PAGE_SIZE = 1 << LOG2_PAGE_SIZE; /** - * a bitpmap font object + * a bitmap font object * Use me.loader.preload or me.loader.load to load assets * me.loader.preload([ * { name: "arial", type: "binary" src: "data/font/arial.fnt" }, * { name: "arial", type: "image" src: "data/font/arial.png" }, * ]) * @class - * @extends me.Font + * @extends me.Renderable * @memberOf me * @constructor * @param {String} font font name @@ -194,8 +194,6 @@ this.cursorX = 0; this.glyphs = {}; - // The glyph to display for characters not in the font. May be null. - this.missingGlyph = null; // The width of the space character. this.spaceWidth = 0; @@ -346,6 +344,61 @@ this.ascent = -this.ascent; this.down = -this.down; } - } + }, + + setGlyphRegion: function (glyph, image) { + var imageWidth = image.width; + var imageHeight = image.width; + var invertImageWidth = 1 / image.width; + var invertImageHeight = 1 / image.height; + var offsetX = 0, offsetY = 0; + var x = glyph.srcX; + var x2 = glyph.srcX + glyph.width; + var y = glyph.srcY; + var y2 = glyph.srcY + glyph.height; + + if (offsetX > 0) { + x -= offsetX; + if (x < 0) { + glyph.width += x; + glyph.xoffset -= x; + x = 0; + } + x2 -= offsetX; + if (x2 > imageWidth) { + glyph.width -= x2 - imageWidth; + x2 = imageWidth; + } + } + if (offsetY > 0) { + y -= offsetY; + if (y < 0) { + glyph.height += y; + y = 0; + } + y2 -= offsetY; + if (y2 > imageHeight) { + var amount = y2 - imageHeight; + glyph.height -= amount; + glyph.yoffset += amount; + y2 = imageHeight; + } + } + + glyph.u = x * invertImageWidth; + glyph.u2 = x2 * invertImageWidth; + if (this.flipped) { + glyph.v = y * invertImageHeight; + glyph.v2 = y2 * invertImageHeight; + } else { + glyph.v2 = y * invertImageHeight; + glyph.v = y2 * invertImageHeight; + } + }, + + setLineHeight: function (height) { + this.lineHeight = height * this.scale.y; + this.down = this.flipped ? this.lineHeight : -this.lineHeight; + }, }); })(); diff --git a/src/font/bitmapfontcache.js b/src/font/bitmapfontcache.js new file mode 100644 index 0000000000..ce2c7fa274 --- /dev/null +++ b/src/font/bitmapfontcache.js @@ -0,0 +1,23 @@ +/* + * MelonJS Game Engine + * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod + * http://www.melonjs.org + * + * Font / Bitmap font + * + * -> first char " " 32d (0x20); + */ +(function () { + /** + * stores a cache of glyph output based on text data and bitmap font data + * @class + * @extends me.Object + * @memberOf me + * @constructor + */ + me.BitmapFontCache = me.Renderable.extend({ + init: function () { + + } + }); +})(); \ No newline at end of file From d8d72faefda2f4f0adef1adb0924e2f37c2ec95b Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Tue, 17 May 2016 22:02:47 -0400 Subject: [PATCH 013/177] Working on cache build. Might need to store array of regions instead. --- src/font/bitmapfontcache.js | 80 ++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/src/font/bitmapfontcache.js b/src/font/bitmapfontcache.js index ce2c7fa274..e3df30726e 100644 --- a/src/font/bitmapfontcache.js +++ b/src/font/bitmapfontcache.js @@ -16,8 +16,86 @@ * @constructor */ me.BitmapFontCache = me.Renderable.extend({ - init: function () { + init: function (bitmapFont, useRoundedPositioning) { + this.bitmapFont = bitmapFont; + this.useRoundedPositioning = useRoundedPositioning; + this.vertexDataCount = 0; + this.vertices = []; + this.layouts = []; + }, + _requireGlyphs: function (layout) { + var newGlyphCount = 0; + for (var i = 0; i < layout.runs.length; i++) { + newGlyphCount += layout.runs[i].glyphs.length; + } + + var vertexCount = this.vertexDataCount + newGlyphCount * 20; + if (this.vertices.length < vertexCount) { + var newVertices = new Array(vertexCount); + for (var i = 0; i < this.vertexDataCount; i++) { + newVertices[i] = this.vertices[i]; + } + this.vertices = newVertices; + } + }, + + addToCache: function (layout, x, y) { + this.layouts.push(layout); + this._requireGlyphs(layout); + for (var i = 0; i < layout.runs.length; i++) { + var run = layout.runs[i]; + var glyphs = run.glyphs; + var xAdvances = run.xAdvances; + var gx = x + run.x, gy = y + run.y; + for (var ii = 0; ii < glyphs.length; ii++) { + var glyph = glyphs[ii]; + gx += xAdvances[ii]; + this.addGlyph(glyph, gx, gy); + } + } + }, + + addGlyph: function (glyph, x, y) { + var scaleX = this.font.data.scale.x; + var scaleY = this.font.data.scale.y; + x += glyph.xoffset * scaleX; + y += glyph.yoffset * scaleY; + var width = glyph.width * scaleX, height = glyph.height * scaleY; + var u = glyph.u, u2 = glyph.u2, v = glyph.v, v2 = glyph.v2; + + if (integer) { + x = Math.round(x); + y = Math.round(y); + width = Math.round(width); + height = Math.round(height); + } + var x2 = x + width, y2 = y + height; + + var page = glyph.page; + this.vertexDataCount += 20; + + var vertices = this.vertices; + var idx = this.vertexDataCount; + vertices[idx++] = x; + vertices[idx++] = y; + vertices[idx++] = u; + vertices[idx++] = v; + + vertices[idx++] = x; + vertices[idx++] = y2; + vertices[idx++] = u; + vertices[idx++] = v2; + + vertices[idx++] = x2; + vertices[idx++] = y2; + vertices[idx++] = u2; + vertices[idx++] = v2; + + vertices[idx++] = x2; + vertices[idx++] = y; + vertices[idx++] = u2; + vertices[idx] = v; } }); })(); \ No newline at end of file From f0afd5365fdd35030fad4e0fd7daa7000010e214 Mon Sep 17 00:00:00 2001 From: obiot Date: Wed, 18 May 2016 13:39:12 +0800 Subject: [PATCH 014/177] Bump version to 4.0.0 --- CHANGELOG | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index f8267a6def..641e581404 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,7 @@ Version History --------------- +4.0.0 + 3.1.0 * Audio : exposed a `me.audio.rate` function to allow modifying the playback rate of the specified audio clip. * Audio : added back the possibility to stream audio elements (through the `stream` flag when defining audio assets) diff --git a/package.json b/package.json index 5ac8733012..0c887e7941 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "melonJS", - "version": "3.1.0", + "version": "4.0.0", "description": "MelonJS Game Engine", "homepage": "http://www.melonjs.org/", "repository": { From 21b15e82ce8cfd3d5319b14592a2675e5a18f3a8 Mon Sep 17 00:00:00 2001 From: obiot Date: Wed, 18 May 2016 14:20:18 +0800 Subject: [PATCH 015/177] [#779] simplified/aligned both the Canvas and WebGL renderer API in a more WebGL fashion see comments here : https://github.com/melonjs/melonJS/issues/779#issuecomment-195210102 --- CHANGELOG | 2 ++ src/game.js | 6 ++--- src/renderable/viewport.js | 4 +-- src/video/canvas/canvas_renderer.js | 38 ++++++++++++++--------------- src/video/renderer.js | 2 +- src/video/webgl/webgl_renderer.js | 11 ++++----- 6 files changed, 31 insertions(+), 32 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 641e581404..39efad4670 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,8 @@ Version History --------------- 4.0.0 +* Renderer : simplified/aligned both the Canvas and WebGL renderer API in a more WebGL fashion + 3.1.0 * Audio : exposed a `me.audio.rate` function to allow modifying the playback rate of the specified audio clip. * Audio : added back the possibility to stream audio elements (through the `stream` flag when defining audio assets) diff --git a/src/game.js b/src/game.js index 1cff4646e6..e3c7d37040 100644 --- a/src/game.js +++ b/src/game.js @@ -333,7 +333,7 @@ api.world.transform.translate(-translateX, -translateY); // prepare renderer to draw a new frame - me.video.renderer.prepareSurface(); + me.video.renderer.clear(); // update all objects, // specifying the viewport as the rectangle area to redraw @@ -348,8 +348,8 @@ isDirty = false; - // blit our frame - me.video.renderer.blitSurface(); + // flush/render our frame + me.video.renderer.flush(); }; // return our object diff --git a/src/renderable/viewport.js b/src/renderable/viewport.js index 0f0cb96e7d..3e9cc05ec2 100644 --- a/src/renderable/viewport.js +++ b/src/renderable/viewport.js @@ -495,7 +495,7 @@ draw : function () { // fading effect if (this._fadeIn.tween) { - me.video.renderer.clearSurface(null, this._fadeIn.color); + me.video.renderer.clearColor(this._fadeIn.color); // remove the tween if over if (this._fadeIn.color.alpha === 1.0) { this._fadeIn.tween = null; @@ -506,7 +506,7 @@ // flashing effect if (this._fadeOut.tween) { - me.video.renderer.clearSurface(null, this._fadeOut.color); + me.video.renderer.clearColor(this._fadeOut.color); // remove the tween if over if (this._fadeOut.color.alpha === 0.0) { this._fadeOut.tween = null; diff --git a/src/video/canvas/canvas_renderer.js b/src/video/canvas/canvas_renderer.js index 69fb6a92f9..30da768980 100644 --- a/src/video/canvas/canvas_renderer.js +++ b/src/video/canvas/canvas_renderer.js @@ -64,23 +64,23 @@ /** * prepare the framebuffer for drawing a new frame - * @name prepareSurface + * @name clear * @memberOf me.CanvasRenderer * @function */ - prepareSurface : function () { + clear : function () { if (this.transparent) { - this.clearSurface(null, "rgba(0,0,0,0)", true); + this.clearColor("rgba(0,0,0,0)", true); } }, /** * render the main framebuffer on screen - * @name blitSurface + * @name flush * @memberOf me.CanvasRenderer * @function */ - blitSurface : function () { + flush : function () { if (this.doubleBuffering) { this.context.drawImage( this.backBufferCanvas, 0, 0, @@ -92,25 +92,23 @@ }, /** - * Clear the specified context with the given color - * @name clearSurface + * Clears the main framebuffer with the given color + * @name clearColor * @memberOf me.CanvasRenderer * @function - * @param {Context2d} [ctx=null] Canvas context, defaults to system context if falsy. * @param {me.Color|String} color CSS color. * @param {Boolean} [opaque=false] Allow transparency [default] or clear the surface completely [true] */ - clearSurface : function (ctx, col, opaque) { - if (!ctx) { - ctx = this.backBufferContext2D; - } - var _canvas = ctx.canvas; - ctx.save(); - ctx.setTransform(1, 0, 0, 1, 0, 0); - ctx.globalCompositeOperation = opaque ? "copy" : "source-over"; - ctx.fillStyle = (col instanceof me.Color) ? col.toRGBA() : col; - ctx.fillRect(0, 0, _canvas.width, _canvas.height); - ctx.restore(); + clearColor : function (col, opaque) { + var _ctx = this.backBufferContext2D; + var _canvas = _ctx.canvas; + + _ctx.save(); + _ctx.setTransform(1, 0, 0, 1, 0, 0); + _ctx.globalCompositeOperation = opaque ? "copy" : "source-over"; + _ctx.fillStyle = (col instanceof me.Color) ? col.toRGBA() : col; + _ctx.fillRect(0, 0, _canvas.width, _canvas.height); + _ctx.restore(); }, /** @@ -285,7 +283,7 @@ this.context.globalCompositeOperation = "copy"; } this.setAntiAlias(this.context, this.antiAlias); - this.blitSurface(); + this.flush(); }, /** diff --git a/src/video/renderer.js b/src/video/renderer.js index 7d64d44bc4..4cde5dc407 100644 --- a/src/video/renderer.js +++ b/src/video/renderer.js @@ -113,7 +113,7 @@ /** * @ignore */ - prepareSurface : function () {}, + clear : function () {}, /** * @ignore diff --git a/src/video/webgl/webgl_renderer.js b/src/video/webgl/webgl_renderer.js index 719ee8aa16..0ca0654a9b 100644 --- a/src/video/webgl/webgl_renderer.js +++ b/src/video/webgl/webgl_renderer.js @@ -200,24 +200,23 @@ /** * Flush the compositor to the frame buffer - * @name blitSurface + * @name flush * @memberOf me.WebGLRenderer * @function */ - blitSurface : function () { + flush : function () { this.compositor.flush(); }, /** - * Clears the gl context. Accepts a gl context or defaults to stored gl renderer. - * @name clearSurface + * Clears the gl context with the given color. + * @name clearColor * @memberOf me.WebGLRenderer * @function - * @param {WebGLContext} [ctx=null] For compatibility only. * @param {me.Color|String} color CSS color. * @param {Boolean} [opaque=false] Allow transparency [default] or clear the surface completely [true] */ - clearSurface : function (ctx, col, opaque) { + clearColor : function (col, opaque) { var color = this.globalColor.clone(); var matrix = this.globalMatrix.clone(); this.globalColor.copy(col); From ecead9dc16afe7421f2b96006d81b58b8c9c7a9c Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Thu, 19 May 2016 23:21:00 -0400 Subject: [PATCH 016/177] Move glyph into own class. Delete bitmap font cache. It doen't make sense. --- sourceFiles.json | 2 +- src/font/bitmapfont.js | 38 -------------- src/font/bitmapfontcache.js | 101 ------------------------------------ src/font/glyph.js | 37 +++++++++++++ 4 files changed, 38 insertions(+), 140 deletions(-) delete mode 100644 src/font/bitmapfontcache.js create mode 100644 src/font/glyph.js diff --git a/sourceFiles.json b/sourceFiles.json index 73efc9e00d..dbed55764b 100644 --- a/sourceFiles.json +++ b/sourceFiles.json @@ -41,7 +41,7 @@ "src/loader/loader.js", "src/font/font.js", "src/font/bitmapfont.js", - "src/font/bitmapfontcache.js", + "src/font/glyph.js", "src/audio/audio.js", "src/video/video.js", "src/video/renderer.js", diff --git a/src/font/bitmapfont.js b/src/font/bitmapfont.js index 49bb0ffc3b..7ffc060d4d 100644 --- a/src/font/bitmapfont.js +++ b/src/font/bitmapfont.js @@ -126,44 +126,6 @@ } }); - /** - * a glyph representing a single character in a font - * @class - * @extends me.Glyph - * @memberOf me - * @constructor - */ - me.Glyph = me.Object.extend({ - init: function () { - this.src = new me.Vector2d(); - this.offset = new me.Vector2d(); - this.onResetEvent(); - }, - - onResetEvent: function () { - this.id = 0; - this.src.set(0, 0); - this.width = 0; - this.height = 0; - this.u = 0; - this.v = 0; - this.u2 = 0; - this.v2 = 0; - this.offset.set(0, 0); - this.xadvance = 0; - this.kerning = []; - this.fixedWidth = false; - }, - - setKerning: function (ch, value) { - var page = this.kerning[ch >>> LOG2_PAGE_SIZE]; - if (page === null) { - this.kerning[ch >>> LOG2_PAGE_SIZE] = page = []; - } - page[ch & PAGE_SIZE - 1] = value; - } - }); - /** * Class for storing relevant data from the font file. * @private diff --git a/src/font/bitmapfontcache.js b/src/font/bitmapfontcache.js deleted file mode 100644 index e3df30726e..0000000000 --- a/src/font/bitmapfontcache.js +++ /dev/null @@ -1,101 +0,0 @@ -/* - * MelonJS Game Engine - * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod - * http://www.melonjs.org - * - * Font / Bitmap font - * - * -> first char " " 32d (0x20); - */ -(function () { - /** - * stores a cache of glyph output based on text data and bitmap font data - * @class - * @extends me.Object - * @memberOf me - * @constructor - */ - me.BitmapFontCache = me.Renderable.extend({ - init: function (bitmapFont, useRoundedPositioning) { - this.bitmapFont = bitmapFont; - this.useRoundedPositioning = useRoundedPositioning; - this.vertexDataCount = 0; - this.vertices = []; - this.layouts = []; - }, - - _requireGlyphs: function (layout) { - var newGlyphCount = 0; - for (var i = 0; i < layout.runs.length; i++) { - newGlyphCount += layout.runs[i].glyphs.length; - } - - var vertexCount = this.vertexDataCount + newGlyphCount * 20; - if (this.vertices.length < vertexCount) { - var newVertices = new Array(vertexCount); - for (var i = 0; i < this.vertexDataCount; i++) { - newVertices[i] = this.vertices[i]; - } - this.vertices = newVertices; - } - }, - - addToCache: function (layout, x, y) { - this.layouts.push(layout); - this._requireGlyphs(layout); - for (var i = 0; i < layout.runs.length; i++) { - var run = layout.runs[i]; - var glyphs = run.glyphs; - var xAdvances = run.xAdvances; - var gx = x + run.x, gy = y + run.y; - for (var ii = 0; ii < glyphs.length; ii++) { - var glyph = glyphs[ii]; - gx += xAdvances[ii]; - this.addGlyph(glyph, gx, gy); - } - } - }, - - addGlyph: function (glyph, x, y) { - var scaleX = this.font.data.scale.x; - var scaleY = this.font.data.scale.y; - x += glyph.xoffset * scaleX; - y += glyph.yoffset * scaleY; - var width = glyph.width * scaleX, height = glyph.height * scaleY; - var u = glyph.u, u2 = glyph.u2, v = glyph.v, v2 = glyph.v2; - - if (integer) { - x = Math.round(x); - y = Math.round(y); - width = Math.round(width); - height = Math.round(height); - } - var x2 = x + width, y2 = y + height; - - var page = glyph.page; - this.vertexDataCount += 20; - - var vertices = this.vertices; - var idx = this.vertexDataCount; - vertices[idx++] = x; - vertices[idx++] = y; - vertices[idx++] = u; - vertices[idx++] = v; - - vertices[idx++] = x; - vertices[idx++] = y2; - vertices[idx++] = u; - vertices[idx++] = v2; - - vertices[idx++] = x2; - vertices[idx++] = y2; - vertices[idx++] = u2; - vertices[idx++] = v2; - - vertices[idx++] = x2; - vertices[idx++] = y; - vertices[idx++] = u2; - vertices[idx] = v; - } - }); -})(); \ No newline at end of file diff --git a/src/font/glyph.js b/src/font/glyph.js new file mode 100644 index 0000000000..a246148711 --- /dev/null +++ b/src/font/glyph.js @@ -0,0 +1,37 @@ +/** + * a glyph representing a single character in a font + * @class + * @extends me.Object + * @memberOf me + * @constructor + */ +me.Glyph = me.Object.extend({ + init: function () { + this.src = new me.Vector2d(); + this.offset = new me.Vector2d(); + this.onResetEvent(); + }, + + onResetEvent: function () { + this.id = 0; + this.src.set(0, 0); + this.width = 0; + this.height = 0; + this.u = 0; + this.v = 0; + this.u2 = 0; + this.v2 = 0; + this.offset.set(0, 0); + this.xadvance = 0; + this.kerning = []; + this.fixedWidth = false; + }, + + setKerning: function (ch, value) { + var page = this.kerning[ch >>> LOG2_PAGE_SIZE]; + if (page === null) { + this.kerning[ch >>> LOG2_PAGE_SIZE] = page = []; + } + page[ch & PAGE_SIZE - 1] = value; + } +}); \ No newline at end of file From da3cadc2ae9f4b5f878739cf4719bd43adc56a6a Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Thu, 19 May 2016 23:29:16 -0400 Subject: [PATCH 017/177] Since melon uses regions as positional data, i dont think this function is needed. Will add back if I'm wrong. --- src/font/bitmapfont.js | 50 ------------------------------------------ 1 file changed, 50 deletions(-) diff --git a/src/font/bitmapfont.js b/src/font/bitmapfont.js index 7ffc060d4d..9e3141f9b4 100644 --- a/src/font/bitmapfont.js +++ b/src/font/bitmapfont.js @@ -308,56 +308,6 @@ } }, - setGlyphRegion: function (glyph, image) { - var imageWidth = image.width; - var imageHeight = image.width; - var invertImageWidth = 1 / image.width; - var invertImageHeight = 1 / image.height; - var offsetX = 0, offsetY = 0; - var x = glyph.srcX; - var x2 = glyph.srcX + glyph.width; - var y = glyph.srcY; - var y2 = glyph.srcY + glyph.height; - - if (offsetX > 0) { - x -= offsetX; - if (x < 0) { - glyph.width += x; - glyph.xoffset -= x; - x = 0; - } - x2 -= offsetX; - if (x2 > imageWidth) { - glyph.width -= x2 - imageWidth; - x2 = imageWidth; - } - } - if (offsetY > 0) { - y -= offsetY; - if (y < 0) { - glyph.height += y; - y = 0; - } - y2 -= offsetY; - if (y2 > imageHeight) { - var amount = y2 - imageHeight; - glyph.height -= amount; - glyph.yoffset += amount; - y2 = imageHeight; - } - } - - glyph.u = x * invertImageWidth; - glyph.u2 = x2 * invertImageWidth; - if (this.flipped) { - glyph.v = y * invertImageHeight; - glyph.v2 = y2 * invertImageHeight; - } else { - glyph.v2 = y * invertImageHeight; - glyph.v = y2 * invertImageHeight; - } - }, - setLineHeight: function (height) { this.lineHeight = height * this.scale.y; this.down = this.flipped ? this.lineHeight : -this.lineHeight; From 5d146478078d1f3a7cb2637b0907a26b66dc4762 Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Thu, 19 May 2016 23:31:10 -0400 Subject: [PATCH 018/177] move constants into glyph file --- src/font/bitmapfont.js | 3 -- src/font/glyph.js | 77 ++++++++++++++++++++++++------------------ 2 files changed, 44 insertions(+), 36 deletions(-) diff --git a/src/font/bitmapfont.js b/src/font/bitmapfont.js index 9e3141f9b4..2fde443326 100644 --- a/src/font/bitmapfont.js +++ b/src/font/bitmapfont.js @@ -8,9 +8,6 @@ * -> first char " " 32d (0x20); */ (function () { - var LOG2_PAGE_SIZE = 9; - var PAGE_SIZE = 1 << LOG2_PAGE_SIZE; - /** * a bitmap font object * Use me.loader.preload or me.loader.load to load assets diff --git a/src/font/glyph.js b/src/font/glyph.js index a246148711..2a71701b82 100644 --- a/src/font/glyph.js +++ b/src/font/glyph.js @@ -1,37 +1,48 @@ -/** - * a glyph representing a single character in a font - * @class - * @extends me.Object - * @memberOf me - * @constructor +/* + * MelonJS Game Engine + * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod + * http://www.melonjs.org + * + * Glyph */ -me.Glyph = me.Object.extend({ - init: function () { - this.src = new me.Vector2d(); - this.offset = new me.Vector2d(); - this.onResetEvent(); - }, +(function() { + var LOG2_PAGE_SIZE = 9; + var PAGE_SIZE = 1 << LOG2_PAGE_SIZE; + /** + * a glyph representing a single character in a font + * @class + * @extends me.Object + * @memberOf me + * @constructor + */ + me.Glyph = me.Object.extend({ + init: function () { + this.src = new me.Vector2d(); + this.offset = new me.Vector2d(); + this.onResetEvent(); + }, - onResetEvent: function () { - this.id = 0; - this.src.set(0, 0); - this.width = 0; - this.height = 0; - this.u = 0; - this.v = 0; - this.u2 = 0; - this.v2 = 0; - this.offset.set(0, 0); - this.xadvance = 0; - this.kerning = []; - this.fixedWidth = false; - }, + onResetEvent: function () { + this.id = 0; + this.src.set(0, 0); + this.width = 0; + this.height = 0; + this.u = 0; + this.v = 0; + this.u2 = 0; + this.v2 = 0; + this.offset.set(0, 0); + this.xadvance = 0; + this.kerning = []; + this.fixedWidth = false; + }, - setKerning: function (ch, value) { - var page = this.kerning[ch >>> LOG2_PAGE_SIZE]; - if (page === null) { - this.kerning[ch >>> LOG2_PAGE_SIZE] = page = []; + setKerning: function (ch, value) { + var page = this.kerning[ch >>> LOG2_PAGE_SIZE]; + if (page === null) { + this.kerning[ch >>> LOG2_PAGE_SIZE] = page = []; + } + page[ch & PAGE_SIZE - 1] = value; } - page[ch & PAGE_SIZE - 1] = value; - } -}); \ No newline at end of file + }); +})(); \ No newline at end of file From 240445b19525336b376bf231b205a5463e6954fa Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Fri, 20 May 2016 22:56:34 -0400 Subject: [PATCH 019/177] Adding original draw back in, starting to modify. --- src/font/bitmapfont.js | 104 +++++++++++++++++++++++++++++++++++++++-- src/font/glyph.js | 10 ++++ 2 files changed, 111 insertions(+), 3 deletions(-) diff --git a/src/font/bitmapfont.js b/src/font/bitmapfont.js index 2fde443326..3adf35b289 100644 --- a/src/font/bitmapfont.js +++ b/src/font/bitmapfont.js @@ -88,16 +88,36 @@ this.fontScale.set(scale, scale); }, + /** + * Does not account for \n + * @name measureTextLineWidth + * @memberOf me.BitmapFont + * @function + * @param {String} text + * @return {Number} the calculated width + */ + measureTextWidth : function(text) { + var characters = text.split(""); + var width = 0; + var lastGlyph = null; + for (var i = 0; i < characters.length; i++) { + var ch = characters[i].charCodeAt(0); + var glyph = this.bitmapFontData.glyphs[ch]; + width += glyph.xadvance + (lastGlyph ? lastGlyph.getKerning(ch) : 0); + } + + return width; + }, + /** * measure the given text size in pixels * @name measureText * @memberOf me.BitmapFont * @function - * @param {me.CanvasRenderer|me.WebGLRenderer} renderer Reference to the destination renderer instance * @param {String} text * @return {Object} an object with two properties: `width` and `height`, defining the output dimensions */ - measureText : function (renderer, text) { + measureText : function (text) { var strings = ("" + text).split("\n"); this.height = this.width = 0; @@ -118,8 +138,73 @@ * @param {String} text * @param {Number} x * @param {Number} y + * @param {Number} width [optional] - the width at which to wrap the text */ - draw : function () { // renderer, text, x, y + draw : function (renderer, text, x, y, width) { + var strings = ("" + text).split("\n"); + var lX = x; + var height = this.sSize.y * this.lineHeight; + + // save the previous global alpha value + var _alpha = renderer.globalAlpha(); + renderer.setGlobalAlpha(_alpha * this.getOpacity()); + + // update initial position + this.pos.set(x, y, this.pos.z); // TODO : z ? + for (var i = 0; i < strings.length; i++) { + x = lX; + var string = strings[i].trimRight(); + // adjust x pos based on alignment value + var width = this.measureTextWidth(string); + switch (this.textAlign) { + case "right": + x -= width; + break; + + case "center": + x -= width * 0.5; + break; + + default : + break; + } + + // adjust y pos based on alignment value + switch (this.textBaseline) { + case "middle": + y -= height * 0.5; + break; + + case "ideographic": + case "alphabetic": + case "bottom": + y -= height; + break; + + default : + break; + } + + // draw the string + for (var c = 0, len = string.length; c < len; c++) { + // calculate the char index + var idx = string.charCodeAt(c) - this.firstChar; + if (idx >= 0) { + // draw it + renderer.drawImage(this.font, + this.fontSize.x * (idx % this.charCount), + this.fontSize.y * ~~(idx / this.charCount), + this.fontSize.x, this.fontSize.y, + ~~x, ~~y, + this.sSize.x, this.sSize.y); + } + x += this.sSize.x; + } + // increment line + y += height; + } + // restore the previous global alpha value + renderer.setGlobalAlpha(_alpha); } }); @@ -152,6 +237,12 @@ // file, it needs to be set manually depending on how the glyphs are rendered on the backing textures. this.cursorX = 0; + /** + * The map of glyphs, each key is a char code. + * @name glyphs + * @property + * @memberOf me.BitmapFontData + */ this.glyphs = {}; // The width of the space character. @@ -194,6 +285,13 @@ return value[0].split("=")[1]; }, + /** + * This parses the font data text and builds a map of glyphs containing the data for each character + * @name parse + * @memberOf me.BitmapFontData + * @function + * @param {String} fontData + */ parse: function (fontData) { if (!fontData) { throw "File containing font data was empty, cannot load the bitmap font."; diff --git a/src/font/glyph.js b/src/font/glyph.js index 2a71701b82..80ae4c6336 100644 --- a/src/font/glyph.js +++ b/src/font/glyph.js @@ -37,6 +37,16 @@ this.fixedWidth = false; }, + getKerning: function (ch, value) { + if (this.kerning) { + var page = this.kerning[ch >>> LOG2_PAGE_SIZE]; + if (page) { + return page[ch & PAGE_SIZE - 1]; + } + } + return 0; + }, + setKerning: function (ch, value) { var page = this.kerning[ch >>> LOG2_PAGE_SIZE]; if (page === null) { From 17835fd18532aa788c69a312b8f49e264a0a8634 Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Sat, 21 May 2016 14:03:35 -0400 Subject: [PATCH 020/177] Basic bitmap font working. Still need to implement scale. --- examples/font_test/arialfancy.fnt | 138 ++++++++++++++++ examples/font_test/arialfancy.png | Bin 0 -> 30835 bytes examples/font_test/main.js | 83 ++++++---- examples/font_test/xolo12.fnt | 2 +- sourceFiles.json | 1 + src/font/bitmapfont.js | 259 ++++-------------------------- src/font/bitmapfontdata.js | 191 ++++++++++++++++++++++ src/font/glyph.js | 27 ++-- testSpecs.json | 3 +- tests/index.html | 1 + tests/spec/bitmapfontdata-spec.js | 170 ++++++++++++++++++++ 11 files changed, 597 insertions(+), 278 deletions(-) create mode 100644 examples/font_test/arialfancy.fnt create mode 100644 examples/font_test/arialfancy.png create mode 100644 src/font/bitmapfontdata.js create mode 100644 tests/spec/bitmapfontdata-spec.js diff --git a/examples/font_test/arialfancy.fnt b/examples/font_test/arialfancy.fnt new file mode 100644 index 0000000000..52d45beb15 --- /dev/null +++ b/examples/font_test/arialfancy.fnt @@ -0,0 +1,138 @@ +info face="Arial" size=18 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=0,0 +common lineHeight=22 base=19 scaleW=256 scaleH=256 pages=1 packed=0 +page id=0 file="arialfancy.png" +chars count=95 +char id=32 x=24 y=20 width=0 height=0 xoffset=0 yoffset=17 xadvance=6 page=0 chnl=0 +char id=33 x=192 y=38 width=8 height=16 xoffset=2 yoffset=3 xadvance=6 page=0 chnl=0 +char id=34 x=242 y=46 width=8 height=8 xoffset=3 yoffset=2 xadvance=7 page=0 chnl=0 +char id=35 x=104 y=38 width=12 height=16 xoffset=1 yoffset=3 xadvance=11 page=0 chnl=0 +char id=36 x=18 y=60 width=12 height=18 xoffset=1 yoffset=2 xadvance=11 page=0 chnl=0 +char id=37 x=230 y=2 width=16 height=16 xoffset=2 yoffset=3 xadvance=17 page=0 chnl=0 +char id=38 x=18 y=42 width=14 height=16 xoffset=2 yoffset=3 xadvance=13 page=0 chnl=0 +char id=39 x=48 y=124 width=6 height=8 xoffset=3 yoffset=2 xadvance=4 page=0 chnl=0 +char id=40 x=48 y=78 width=8 height=20 xoffset=2 yoffset=2 xadvance=6 page=0 chnl=0 +char id=41 x=48 y=56 width=8 height=20 xoffset=0 yoffset=2 xadvance=6 page=0 chnl=0 +char id=42 x=232 y=46 width=8 height=8 xoffset=3 yoffset=3 xadvance=8 page=0 chnl=0 +char id=43 x=226 y=24 width=12 height=12 xoffset=2 yoffset=4 xadvance=11 page=0 chnl=0 +char id=44 x=48 y=114 width=6 height=8 xoffset=1 yoffset=13 xadvance=6 page=0 chnl=0 +char id=45 x=212 y=50 width=8 height=4 xoffset=1 yoffset=11 xadvance=6 page=0 chnl=0 +char id=46 x=48 y=134 width=6 height=4 xoffset=2 yoffset=15 xadvance=6 page=0 chnl=0 +char id=47 x=90 y=38 width=12 height=16 xoffset=0 yoffset=3 xadvance=6 page=0 chnl=0 +char id=48 x=76 y=38 width=12 height=16 xoffset=2 yoffset=3 xadvance=11 page=0 chnl=0 +char id=49 x=182 y=38 width=8 height=16 xoffset=3 yoffset=3 xadvance=11 page=0 chnl=0 +char id=50 x=62 y=38 width=12 height=16 xoffset=2 yoffset=3 xadvance=11 page=0 chnl=0 +char id=51 x=48 y=38 width=12 height=16 xoffset=1 yoffset=3 xadvance=11 page=0 chnl=0 +char id=52 x=34 y=236 width=12 height=16 xoffset=1 yoffset=3 xadvance=11 page=0 chnl=0 +char id=53 x=34 y=218 width=12 height=16 xoffset=2 yoffset=3 xadvance=11 page=0 chnl=0 +char id=54 x=34 y=200 width=12 height=16 xoffset=2 yoffset=3 xadvance=11 page=0 chnl=0 +char id=55 x=34 y=182 width=12 height=16 xoffset=3 yoffset=3 xadvance=11 page=0 chnl=0 +char id=56 x=34 y=164 width=12 height=16 xoffset=2 yoffset=3 xadvance=11 page=0 chnl=0 +char id=57 x=34 y=146 width=12 height=16 xoffset=2 yoffset=3 xadvance=11 page=0 chnl=0 +char id=58 x=48 y=100 width=6 height=12 xoffset=2 yoffset=7 xadvance=6 page=0 chnl=0 +char id=59 x=202 y=38 width=8 height=14 xoffset=1 yoffset=7 xadvance=6 page=0 chnl=0 +char id=60 x=212 y=24 width=12 height=12 xoffset=2 yoffset=5 xadvance=11 page=0 chnl=0 +char id=61 x=18 y=244 width=12 height=8 xoffset=2 yoffset=7 xadvance=11 page=0 chnl=0 +char id=62 x=198 y=24 width=12 height=12 xoffset=2 yoffset=5 xadvance=11 page=0 chnl=0 +char id=63 x=130 y=38 width=10 height=16 xoffset=3 yoffset=3 xadvance=11 page=0 chnl=0 +char id=64 x=2 y=2 width=20 height=20 xoffset=1 yoffset=2 xadvance=19 page=0 chnl=0 +char id=65 x=18 y=24 width=14 height=16 xoffset=0 yoffset=3 xadvance=13 page=0 chnl=0 +char id=66 x=2 y=222 width=14 height=16 xoffset=1 yoffset=3 xadvance=13 page=0 chnl=0 +char id=67 x=2 y=204 width=14 height=16 xoffset=2 yoffset=3 xadvance=13 page=0 chnl=0 +char id=68 x=212 y=2 width=16 height=16 xoffset=1 yoffset=3 xadvance=13 page=0 chnl=0 +char id=69 x=2 y=186 width=14 height=16 xoffset=1 yoffset=3 xadvance=13 page=0 chnl=0 +char id=70 x=2 y=168 width=14 height=16 xoffset=1 yoffset=3 xadvance=11 page=0 chnl=0 +char id=71 x=194 y=2 width=16 height=16 xoffset=2 yoffset=3 xadvance=15 page=0 chnl=0 +char id=72 x=176 y=2 width=16 height=16 xoffset=1 yoffset=3 xadvance=13 page=0 chnl=0 +char id=73 x=172 y=38 width=8 height=16 xoffset=2 yoffset=3 xadvance=6 page=0 chnl=0 +char id=74 x=34 y=128 width=12 height=16 xoffset=1 yoffset=3 xadvance=9 page=0 chnl=0 +char id=75 x=158 y=2 width=16 height=16 xoffset=1 yoffset=3 xadvance=13 page=0 chnl=0 +char id=76 x=34 y=110 width=12 height=16 xoffset=1 yoffset=3 xadvance=11 page=0 chnl=0 +char id=77 x=84 y=2 width=18 height=16 xoffset=1 yoffset=3 xadvance=15 page=0 chnl=0 +char id=78 x=140 y=2 width=16 height=16 xoffset=1 yoffset=3 xadvance=13 page=0 chnl=0 +char id=79 x=122 y=2 width=16 height=16 xoffset=2 yoffset=3 xadvance=15 page=0 chnl=0 +char id=80 x=2 y=150 width=14 height=16 xoffset=1 yoffset=3 xadvance=13 page=0 chnl=0 +char id=81 x=66 y=2 width=16 height=18 xoffset=2 yoffset=2 xadvance=15 page=0 chnl=0 +char id=82 x=104 y=2 width=16 height=16 xoffset=1 yoffset=3 xadvance=13 page=0 chnl=0 +char id=83 x=2 y=132 width=14 height=16 xoffset=2 yoffset=3 xadvance=13 page=0 chnl=0 +char id=84 x=2 y=114 width=14 height=16 xoffset=3 yoffset=3 xadvance=11 page=0 chnl=0 +char id=85 x=2 y=96 width=14 height=16 xoffset=2 yoffset=3 xadvance=13 page=0 chnl=0 +char id=86 x=2 y=78 width=14 height=16 xoffset=3 yoffset=3 xadvance=13 page=0 chnl=0 +char id=87 x=24 y=2 width=20 height=16 xoffset=3 yoffset=3 xadvance=17 page=0 chnl=0 +char id=88 x=46 y=2 width=18 height=16 xoffset=0 yoffset=3 xadvance=13 page=0 chnl=0 +char id=89 x=2 y=60 width=14 height=16 xoffset=3 yoffset=3 xadvance=13 page=0 chnl=0 +char id=90 x=2 y=42 width=14 height=16 xoffset=1 yoffset=3 xadvance=11 page=0 chnl=0 +char id=91 x=18 y=168 width=10 height=20 xoffset=1 yoffset=2 xadvance=6 page=0 chnl=0 +char id=92 x=224 y=38 width=6 height=16 xoffset=2 yoffset=3 xadvance=6 page=0 chnl=0 +char id=93 x=18 y=146 width=10 height=20 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=0 +char id=94 x=212 y=38 width=10 height=10 xoffset=2 yoffset=2 xadvance=9 page=0 chnl=0 +char id=95 x=58 y=56 width=14 height=4 xoffset=-1 yoffset=18 xadvance=11 page=0 chnl=0 +char id=96 x=246 y=38 width=6 height=6 xoffset=3 yoffset=2 xadvance=6 page=0 chnl=0 +char id=97 x=184 y=24 width=12 height=12 xoffset=1 yoffset=7 xadvance=11 page=0 chnl=0 +char id=98 x=34 y=92 width=12 height=16 xoffset=1 yoffset=3 xadvance=11 page=0 chnl=0 +char id=99 x=170 y=24 width=12 height=12 xoffset=2 yoffset=7 xadvance=9 page=0 chnl=0 +char id=100 x=34 y=74 width=12 height=16 xoffset=1 yoffset=3 xadvance=11 page=0 chnl=0 +char id=101 x=156 y=24 width=12 height=12 xoffset=1 yoffset=7 xadvance=11 page=0 chnl=0 +char id=102 x=118 y=38 width=10 height=16 xoffset=1 yoffset=3 xadvance=6 page=0 chnl=0 +char id=103 x=34 y=56 width=12 height=16 xoffset=1 yoffset=6 xadvance=11 page=0 chnl=0 +char id=104 x=34 y=38 width=12 height=16 xoffset=1 yoffset=3 xadvance=11 page=0 chnl=0 +char id=105 x=162 y=38 width=8 height=16 xoffset=1 yoffset=3 xadvance=4 page=0 chnl=0 +char id=106 x=18 y=124 width=10 height=20 xoffset=-2 yoffset=2 xadvance=4 page=0 chnl=0 +char id=107 x=18 y=226 width=12 height=16 xoffset=1 yoffset=3 xadvance=9 page=0 chnl=0 +char id=108 x=152 y=38 width=8 height=16 xoffset=1 yoffset=3 xadvance=4 page=0 chnl=0 +char id=109 x=34 y=24 width=18 height=12 xoffset=1 yoffset=7 xadvance=15 page=0 chnl=0 +char id=110 x=142 y=24 width=12 height=12 xoffset=1 yoffset=7 xadvance=11 page=0 chnl=0 +char id=111 x=128 y=24 width=12 height=12 xoffset=1 yoffset=7 xadvance=11 page=0 chnl=0 +char id=112 x=18 y=208 width=12 height=16 xoffset=0 yoffset=6 xadvance=11 page=0 chnl=0 +char id=113 x=18 y=190 width=12 height=16 xoffset=1 yoffset=6 xadvance=11 page=0 chnl=0 +char id=114 x=240 y=24 width=10 height=12 xoffset=1 yoffset=7 xadvance=6 page=0 chnl=0 +char id=115 x=114 y=24 width=12 height=12 xoffset=1 yoffset=7 xadvance=9 page=0 chnl=0 +char id=116 x=142 y=38 width=8 height=16 xoffset=2 yoffset=3 xadvance=6 page=0 chnl=0 +char id=117 x=100 y=24 width=12 height=12 xoffset=2 yoffset=7 xadvance=11 page=0 chnl=0 +char id=118 x=86 y=24 width=12 height=12 xoffset=2 yoffset=7 xadvance=9 page=0 chnl=0 +char id=119 x=54 y=24 width=16 height=12 xoffset=2 yoffset=7 xadvance=13 page=0 chnl=0 +char id=120 x=72 y=24 width=12 height=12 xoffset=0 yoffset=7 xadvance=9 page=0 chnl=0 +char id=121 x=2 y=24 width=14 height=16 xoffset=0 yoffset=6 xadvance=9 page=0 chnl=0 +char id=122 x=2 y=240 width=12 height=12 xoffset=1 yoffset=7 xadvance=9 page=0 chnl=0 +char id=123 x=18 y=102 width=10 height=20 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=0 +char id=124 x=248 y=2 width=4 height=20 xoffset=2 yoffset=2 xadvance=5 page=0 chnl=0 +char id=125 x=18 y=80 width=10 height=20 xoffset=-1 yoffset=2 xadvance=7 page=0 chnl=0 +char id=126 x=232 y=38 width=12 height=6 xoffset=2 yoffset=8 xadvance=11 page=0 chnl=0 +kernings count=38 +kerning first=84 second=97 amount=-1 +kerning first=84 second=99 amount=-1 +kerning first=80 second=65 amount=-1 +kerning first=89 second=111 amount=-1 +kerning first=84 second=111 amount=-1 +kerning first=89 second=112 amount=-1 +kerning first=84 second=65 amount=-1 +kerning first=80 second=44 amount=-2 +kerning first=89 second=113 amount=-1 +kerning first=76 second=84 amount=-1 +kerning first=80 second=46 amount=-2 +kerning first=76 second=86 amount=-1 +kerning first=76 second=87 amount=-1 +kerning first=84 second=58 amount=-1 +kerning first=84 second=44 amount=-1 +kerning first=84 second=115 amount=-1 +kerning first=76 second=89 amount=-1 +kerning first=84 second=46 amount=-1 +kerning first=89 second=97 amount=-1 +kerning first=89 second=101 amount=-1 +kerning first=49 second=49 amount=-1 +kerning first=84 second=101 amount=-1 +kerning first=86 second=97 amount=-1 +kerning first=89 second=65 amount=-1 +kerning first=118 second=44 amount=-1 +kerning first=65 second=84 amount=-1 +kerning first=65 second=86 amount=-1 +kerning first=118 second=46 amount=-1 +kerning first=65 second=89 amount=-1 +kerning first=89 second=44 amount=-2 +kerning first=86 second=65 amount=-1 +kerning first=89 second=46 amount=-2 +kerning first=121 second=44 amount=-1 +kerning first=70 second=44 amount=-1 +kerning first=86 second=44 amount=-1 +kerning first=70 second=46 amount=-1 +kerning first=121 second=46 amount=-1 +kerning first=86 second=46 amount=-1 diff --git a/examples/font_test/arialfancy.png b/examples/font_test/arialfancy.png new file mode 100644 index 0000000000000000000000000000000000000000..8f487d35126a59108f7f8ae4966900c439ea7151 GIT binary patch literal 30835 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 0) { - // draw it - renderer.drawImage(this.font, - this.fontSize.x * (idx % this.charCount), - this.fontSize.y * ~~(idx / this.charCount), - this.fontSize.x, this.fontSize.y, - ~~x, ~~y, - this.sSize.x, this.sSize.y); - } - x += this.sSize.x; + var ch = string.charCodeAt(c); + var glyph = this.bitmapFontData.glyphs[ch]; + + // draw it + renderer.drawImage(this.fontImage, + glyph.src.x, glyph.src.y, + glyph.width, glyph.height, + ~~x, ~~y + glyph.offset.y, + glyph.width, glyph.height); + x += glyph.xadvance + (lastGlyph ? lastGlyph.getKerning(ch) : 0); + lastGlyph = glyph; } // increment line - y += height; + y += stringHeight; } // restore the previous global alpha value renderer.setGlobalAlpha(_alpha); } }); - - /** - * Class for storing relevant data from the font file. - * @private - * @class me.BitmapFontData - * @memberOf me - * @constructor - */ - me.BitmapFontData = me.Object.extend({ - init: function (flipped) { - this.flipped = flipped; - this.padTop = 0; - this.padRight = 0; - this.padBottom = 0; - this.padLeft = 0; - // The distance from one line of text to the next. To set this value, use {@link #setLineHeight(float)}. - this.lineHeight = 0; - // The distance from the top of most uppercase characters to the baseline. Since the drawing position is the cap height of - // the first line, the cap height can be used to get the location of the baseline. - this.capHeight = 1; - // The distance from the cap height to the top of the tallest glyph. - this.ascent = 0; - // The distance from the bottom of the glyph that extends the lowest to the baseline. This number is negative. - this.descent = 0; - this.down = 0; - this.scale = new me.Vector2d(); - // The amount to add to the glyph X position when drawing a cursor between glyphs. This field is not set by the BMFont - // file, it needs to be set manually depending on how the glyphs are rendered on the backing textures. - this.cursorX = 0; - - /** - * The map of glyphs, each key is a char code. - * @name glyphs - * @property - * @memberOf me.BitmapFontData - */ - this.glyphs = {}; - - // The width of the space character. - this.spaceWidth = 0; - // The x-height, which is the distance from the top of most lowercase characters to the baseline. - this.xHeight = 1; - - this.xChars = ["x", "e", "a", "o", "n", "s", "r", "c", "u", "m", "v", "w", "z"]; - this.capChars = ["M", "N", "B", "D", "C", "E", "F", "K", "A", "G", "H", "I", "J", "L", "O", "P", "Q", "R", "S", - "T", "U", "V", "W", "X", "Y", "Z"]; - }, - - _createSpaceGlyph: function () { - var spaceCharCode = " ".charCodeAt(0); - if (!this.glyphs[spaceCharCode]) { - var glyph = me.pool.pull("me.Glyph"); - glyph.id = spaceCharCode; - glyph.xadvance = this._getFirstGlyph().xadvance; - this.glyphs[spaceCharCode] = glyph; - - if (glyph.width === 0) { - glyph.width = ~~(this.padLeft + glyph.xadvance + this.padRight); - glyph.offset.set(-this.padLeft, 0); - } - - this.spaceWidth = glyph.width; - } - }, - - _getFirstGlyph: function () { - return this.glyphs[Object.keys(this.glyphs)[0]]; - }, - - _getValueFromPair: function (string, pattern) { - var value = string.match(pattern); - if (!value) { - throw "Could not find pattern " + pattern + " in string: " + string; - } - - return value[0].split("=")[1]; - }, - - /** - * This parses the font data text and builds a map of glyphs containing the data for each character - * @name parse - * @memberOf me.BitmapFontData - * @function - * @param {String} fontData - */ - parse: function (fontData) { - if (!fontData) { - throw "File containing font data was empty, cannot load the bitmap font."; - } - var lines = fontData.split(/\r\n|\n/); - var padding = fontData.match(/padding\=\d+,\d+,\d+,\d+/g); - if (!padding) { - throw "Padding not found in first line"; - } - var paddingValues = padding[0].split("=")[1].split(","); - this.padTop = parseFloat(paddingValues[0]); - this.padLeft = parseFloat(paddingValues[1]); - this.padBottom = parseFloat(paddingValues[2]); - this.padRight = parseFloat(paddingValues[3]); - - this.lineHeight = parseFloat(this._getValueFromPair(lines[1], /lineHeight\=\d+/g)); - - var baseLine = parseFloat(this._getValueFromPair(lines[1], /base\=\d+/g)); - - var padY = this.padTop + this.padBottom; - - var glyph = null; - - for (var i = 4; i < lines.length; i++) { - var line = lines[i]; - var characterValues = line.split(/=|\s/); - if (!line || /^kernings/.test(line)) { - continue; - } - if (/^kerning\s/.test(line)) { - var first = parseFloat(characterValues[2]); - var second = parseFloat(characterValues[4]); - var amount = parseFloat(characterValues[6]); - - glyph = this.glyphs[first]; - if (glyph !== null && typeof glyph !== "undefined") { - glyph.setKerning(second, amount); - } - } else { - glyph = me.pool.pull("me.Glyph"); - - var ch = parseFloat(characterValues[2]); - glyph.id = ch; - glyph.src.set(parseFloat(characterValues[4]), parseFloat(characterValues[6])); - glyph.width = parseFloat(characterValues[8]); - glyph.height = parseFloat(characterValues[10]); - var y = parseFloat(characterValues[14]); - if (this.flipped) { - y = -(glyph.height + parseFloat(characterValues[14])); - } - glyph.offset.set(parseFloat(characterValues[12]), y); - - glyph.xadvance = parseFloat(characterValues[16]); - - if (glyph.width > 0 && glyph.height > 0) { - this.descent = Math.min(baseLine + glyph.yoffset, this.descent); - } - - this.glyphs[ch] = glyph; - } - } - - this.descent += this.padBottom; - - this._createSpaceGlyph(); - - var xGlyph = null; - for (i = 0; i < this.xChars.length; i++) { - var xChar = this.xChars[i]; - xGlyph = this.glyphs[xChar.charCodeAt(0)]; - if (xGlyph) { - break; - } - } - if (!xGlyph) { - xGlyph = this._getFirstGlyph(); - } - - var capGlyph = null; - for (i = 0; i < this.capChars.length; i++) { - var capChar = this.capChars[i]; - capGlyph = this.glyphs[capChar.charCodeAt(0)]; - if (capGlyph) { - break; - } - } - if (!capGlyph) { - for (var charCode in this.glyphs) { - if (this.glyphs.hasOwnProperty(charCode)) { - glyph = this.glyphs[charCode]; - if (glyph.height === 0 || glyph.width === 0) { - continue; - } - this.capHeight = Math.max(this.capHeight, glyph.height); - } - } - } else { - this.capHeight = capGlyph.height; - } - - this.capHeight -= padY; - - this.ascent = this.baseLine - this.capHeight; - this.down = -this.lineHeight; - - if (this.flipped) { - this.ascent = -this.ascent; - this.down = -this.down; - } - }, - - setLineHeight: function (height) { - this.lineHeight = height * this.scale.y; - this.down = this.flipped ? this.lineHeight : -this.lineHeight; - }, - }); })(); diff --git a/src/font/bitmapfontdata.js b/src/font/bitmapfontdata.js new file mode 100644 index 0000000000..c2c16b34a1 --- /dev/null +++ b/src/font/bitmapfontdata.js @@ -0,0 +1,191 @@ +/** + * Class for storing relevant data from the font file. + * @private + * @class me.BitmapFontData + * @memberOf me + * @constructor + */ +me.BitmapFontData = me.Object.extend({ + init: function () { + this.padTop = 0; + this.padRight = 0; + this.padBottom = 0; + this.padLeft = 0; + // The distance from one line of text to the next. To set this value, use {@link #setLineHeight(float)}. + this.lineHeight = 0; + // The distance from the top of most uppercase characters to the baseline. Since the drawing position is the cap height of + // the first line, the cap height can be used to get the location of the baseline. + this.capHeight = 1; + // The distance from the cap height to the top of the tallest glyph. + this.ascent = 0; + // The distance from the bottom of the glyph that extends the lowest to the baseline. This number is negative. + this.descent = 0; + this.down = 0; + this.scale = new me.Vector2d(); + // The amount to add to the glyph X position when drawing a cursor between glyphs. This field is not set by the BMFont + // file, it needs to be set manually depending on how the glyphs are rendered on the backing textures. + this.cursorX = 0; + + /** + * The map of glyphs, each key is a char code. + * @name glyphs + * @property + * @memberOf me.BitmapFontData + */ + this.glyphs = {}; + + // The width of the space character. + this.spaceWidth = 0; + // The x-height, which is the distance from the top of most lowercase characters to the baseline. + this.xHeight = 1; + + this.xChars = ["x", "e", "a", "o", "n", "s", "r", "c", "u", "m", "v", "w", "z"]; + this.capChars = ["M", "N", "B", "D", "C", "E", "F", "K", "A", "G", "H", "I", "J", "L", "O", "P", "Q", "R", "S", + "T", "U", "V", "W", "X", "Y", "Z"]; + }, + + _createSpaceGlyph: function () { + var spaceCharCode = " ".charCodeAt(0); + if (!this.glyphs[spaceCharCode]) { + var glyph = me.pool.pull("me.Glyph"); + glyph.id = spaceCharCode; + glyph.xadvance = this._getFirstGlyph().xadvance; + this.glyphs[spaceCharCode] = glyph; + + if (glyph.width === 0) { + glyph.width = ~~(this.padLeft + glyph.xadvance + this.padRight); + glyph.offset.set(-this.padLeft, 0); + } + + this.spaceWidth = glyph.width; + } + }, + + _getFirstGlyph: function () { + return this.glyphs[Object.keys(this.glyphs)[0]]; + }, + + _getValueFromPair: function (string, pattern) { + var value = string.match(pattern); + if (!value) { + throw "Could not find pattern " + pattern + " in string: " + string; + } + + return value[0].split("=")[1]; + }, + + /** + * This parses the font data text and builds a map of glyphs containing the data for each character + * @name parse + * @memberOf me.BitmapFontData + * @function + * @param {String} fontData + */ + parse: function (fontData) { + if (!fontData) { + throw "File containing font data was empty, cannot load the bitmap font."; + } + var lines = fontData.split(/\r\n|\n/); + var padding = fontData.match(/padding\=\d+,\d+,\d+,\d+/g); + if (!padding) { + throw "Padding not found in first line"; + } + var paddingValues = padding[0].split("=")[1].split(","); + this.padTop = parseFloat(paddingValues[0]); + this.padLeft = parseFloat(paddingValues[1]); + this.padBottom = parseFloat(paddingValues[2]); + this.padRight = parseFloat(paddingValues[3]); + + this.lineHeight = parseFloat(this._getValueFromPair(lines[1], /lineHeight\=\d+/g)); + + var baseLine = parseFloat(this._getValueFromPair(lines[1], /base\=\d+/g)); + + var padY = this.padTop + this.padBottom; + + var glyph = null; + + for (var i = 4; i < lines.length; i++) { + var line = lines[i]; + var characterValues = line.split(/=|\s/); + if (!line || /^kernings/.test(line)) { + continue; + } + if (/^kerning\s/.test(line)) { + var first = parseFloat(characterValues[2]); + var second = parseFloat(characterValues[4]); + var amount = parseFloat(characterValues[6]); + + glyph = this.glyphs[first]; + if (glyph !== null && typeof glyph !== "undefined") { + glyph.setKerning(second, amount); + } + } else { + glyph = me.pool.pull("me.Glyph"); + + var ch = parseFloat(characterValues[2]); + glyph.id = ch; + glyph.src.set(parseFloat(characterValues[4]), parseFloat(characterValues[6])); + glyph.width = parseFloat(characterValues[8]); + glyph.height = parseFloat(characterValues[10]); + var y = parseFloat(characterValues[14]); + glyph.offset.set(parseFloat(characterValues[12]), y); + + glyph.xadvance = parseFloat(characterValues[16]); + + if (glyph.width > 0 && glyph.height > 0) { + this.descent = Math.min(baseLine + glyph.yoffset, this.descent); + } + + this.glyphs[ch] = glyph; + } + } + + this.descent += this.padBottom; + + this._createSpaceGlyph(); + + var xGlyph = null; + for (i = 0; i < this.xChars.length; i++) { + var xChar = this.xChars[i]; + xGlyph = this.glyphs[xChar.charCodeAt(0)]; + if (xGlyph) { + break; + } + } + if (!xGlyph) { + xGlyph = this._getFirstGlyph(); + } + + var capGlyph = null; + for (i = 0; i < this.capChars.length; i++) { + var capChar = this.capChars[i]; + capGlyph = this.glyphs[capChar.charCodeAt(0)]; + if (capGlyph) { + break; + } + } + if (!capGlyph) { + for (var charCode in this.glyphs) { + if (this.glyphs.hasOwnProperty(charCode)) { + glyph = this.glyphs[charCode]; + if (glyph.height === 0 || glyph.width === 0) { + continue; + } + this.capHeight = Math.max(this.capHeight, glyph.height); + } + } + } else { + this.capHeight = capGlyph.height; + } + + this.capHeight -= padY; + + this.ascent = this.baseLine - this.capHeight; + this.down = -this.lineHeight; + }, + + setLineHeight: function (height) { + this.lineHeight = height * this.scale.y; + this.down = -this.lineHeight; + }, +}); \ No newline at end of file diff --git a/src/font/glyph.js b/src/font/glyph.js index 80ae4c6336..f47a53cac7 100644 --- a/src/font/glyph.js +++ b/src/font/glyph.js @@ -33,26 +33,29 @@ this.v2 = 0; this.offset.set(0, 0); this.xadvance = 0; - this.kerning = []; this.fixedWidth = false; }, - getKerning: function (ch, value) { - if (this.kerning) { - var page = this.kerning[ch >>> LOG2_PAGE_SIZE]; - if (page) { - return page[ch & PAGE_SIZE - 1]; - } - } - return 0; + getKerning: function (ch) { + if (this.kerning) { + var page = this.kerning[ch >>> LOG2_PAGE_SIZE]; + if (page) { + return page[ch & PAGE_SIZE - 1] || 0; + } + } + return 0; }, setKerning: function (ch, value) { + if (!this.kerning) { + this.kerning = {}; + } var page = this.kerning[ch >>> LOG2_PAGE_SIZE]; - if (page === null) { - this.kerning[ch >>> LOG2_PAGE_SIZE] = page = []; + if (typeof page === "undefined") { + this.kerning[ch >>> LOG2_PAGE_SIZE] = {}; + page = this.kerning[ch >>> LOG2_PAGE_SIZE]; } page[ch & PAGE_SIZE - 1] = value; } }); -})(); \ No newline at end of file +})(); diff --git a/testSpecs.json b/testSpecs.json index 6c6ad69eba..de03175919 100644 --- a/testSpecs.json +++ b/testSpecs.json @@ -19,5 +19,6 @@ "tests/spec/plugin-spec.js", "tests/spec/entity-spec.js", "tests/spec/renderable-spec.js", - "tests/spec/font-spec.js" + "tests/spec/font-spec.js", + "tests/spec/bitmapfontdata-spec.js" ] diff --git a/tests/index.html b/tests/index.html index a475d884c8..1556576df7 100644 --- a/tests/index.html +++ b/tests/index.html @@ -41,5 +41,6 @@ + diff --git a/tests/spec/bitmapfontdata-spec.js b/tests/spec/bitmapfontdata-spec.js new file mode 100644 index 0000000000..8e8a3632eb --- /dev/null +++ b/tests/spec/bitmapfontdata-spec.js @@ -0,0 +1,170 @@ +var bitmapFontDataFixture = "info face=\"Arial\" size=18 bold=0 italic=0 charset=\"\" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=0,0\n" + +"common lineHeight=22 base=19 scaleW=256 scaleH=256 pages=1 packed=0\n" + +"page id=0 file=\"arialfancy.png\"\n" + +"chars count=95\n" + +"char id=32 x=24 y=20 width=0 height=0 xoffset=0 yoffset=17 xadvance=6 page=0 chnl=0\n" + +"char id=33 x=192 y=38 width=8 height=16 xoffset=2 yoffset=3 xadvance=6 page=0 chnl=0\n" + +"char id=34 x=242 y=46 width=8 height=8 xoffset=3 yoffset=2 xadvance=7 page=0 chnl=0\n" + +"char id=35 x=104 y=38 width=12 height=16 xoffset=1 yoffset=3 xadvance=11 page=0 chnl=0\n" + +"char id=36 x=18 y=60 width=12 height=18 xoffset=1 yoffset=2 xadvance=11 page=0 chnl=0\n" + +"char id=37 x=230 y=2 width=16 height=16 xoffset=2 yoffset=3 xadvance=17 page=0 chnl=0\n" + +"char id=38 x=18 y=42 width=14 height=16 xoffset=2 yoffset=3 xadvance=13 page=0 chnl=0\n" + +"char id=39 x=48 y=124 width=6 height=8 xoffset=3 yoffset=2 xadvance=4 page=0 chnl=0\n" + +"char id=40 x=48 y=78 width=8 height=20 xoffset=2 yoffset=2 xadvance=6 page=0 chnl=0\n" + +"char id=41 x=48 y=56 width=8 height=20 xoffset=0 yoffset=2 xadvance=6 page=0 chnl=0\n" + +"char id=42 x=232 y=46 width=8 height=8 xoffset=3 yoffset=3 xadvance=8 page=0 chnl=0\n" + +"char id=43 x=226 y=24 width=12 height=12 xoffset=2 yoffset=4 xadvance=11 page=0 chnl=0\n" + +"char id=44 x=48 y=114 width=6 height=8 xoffset=1 yoffset=13 xadvance=6 page=0 chnl=0\n" + +"char id=45 x=212 y=50 width=8 height=4 xoffset=1 yoffset=11 xadvance=6 page=0 chnl=0\n" + +"char id=46 x=48 y=134 width=6 height=4 xoffset=2 yoffset=15 xadvance=6 page=0 chnl=0\n" + +"char id=47 x=90 y=38 width=12 height=16 xoffset=0 yoffset=3 xadvance=6 page=0 chnl=0\n" + +"char id=48 x=76 y=38 width=12 height=16 xoffset=2 yoffset=3 xadvance=11 page=0 chnl=0\n" + +"char id=49 x=182 y=38 width=8 height=16 xoffset=3 yoffset=3 xadvance=11 page=0 chnl=0\n" + +"char id=50 x=62 y=38 width=12 height=16 xoffset=2 yoffset=3 xadvance=11 page=0 chnl=0\n" + +"char id=51 x=48 y=38 width=12 height=16 xoffset=1 yoffset=3 xadvance=11 page=0 chnl=0\n" + +"char id=52 x=34 y=236 width=12 height=16 xoffset=1 yoffset=3 xadvance=11 page=0 chnl=0\n" + +"char id=53 x=34 y=218 width=12 height=16 xoffset=2 yoffset=3 xadvance=11 page=0 chnl=0\n" + +"char id=54 x=34 y=200 width=12 height=16 xoffset=2 yoffset=3 xadvance=11 page=0 chnl=0\n" + +"char id=55 x=34 y=182 width=12 height=16 xoffset=3 yoffset=3 xadvance=11 page=0 chnl=0\n" + +"char id=56 x=34 y=164 width=12 height=16 xoffset=2 yoffset=3 xadvance=11 page=0 chnl=0\n" + +"char id=57 x=34 y=146 width=12 height=16 xoffset=2 yoffset=3 xadvance=11 page=0 chnl=0\n" + +"char id=58 x=48 y=100 width=6 height=12 xoffset=2 yoffset=7 xadvance=6 page=0 chnl=0\n" + +"char id=59 x=202 y=38 width=8 height=14 xoffset=1 yoffset=7 xadvance=6 page=0 chnl=0\n" + +"char id=60 x=212 y=24 width=12 height=12 xoffset=2 yoffset=5 xadvance=11 page=0 chnl=0\n" + +"char id=61 x=18 y=244 width=12 height=8 xoffset=2 yoffset=7 xadvance=11 page=0 chnl=0\n" + +"char id=62 x=198 y=24 width=12 height=12 xoffset=2 yoffset=5 xadvance=11 page=0 chnl=0\n" + +"char id=63 x=130 y=38 width=10 height=16 xoffset=3 yoffset=3 xadvance=11 page=0 chnl=0\n" + +"char id=64 x=2 y=2 width=20 height=20 xoffset=1 yoffset=2 xadvance=19 page=0 chnl=0\n" + +"char id=65 x=18 y=24 width=14 height=16 xoffset=0 yoffset=3 xadvance=13 page=0 chnl=0\n" + +"char id=66 x=2 y=222 width=14 height=16 xoffset=1 yoffset=3 xadvance=13 page=0 chnl=0\n" + +"char id=67 x=2 y=204 width=14 height=16 xoffset=2 yoffset=3 xadvance=13 page=0 chnl=0\n" + +"char id=68 x=212 y=2 width=16 height=16 xoffset=1 yoffset=3 xadvance=13 page=0 chnl=0\n" + +"char id=69 x=2 y=186 width=14 height=16 xoffset=1 yoffset=3 xadvance=13 page=0 chnl=0\n" + +"char id=70 x=2 y=168 width=14 height=16 xoffset=1 yoffset=3 xadvance=11 page=0 chnl=0\n" + +"char id=71 x=194 y=2 width=16 height=16 xoffset=2 yoffset=3 xadvance=15 page=0 chnl=0\n" + +"char id=72 x=176 y=2 width=16 height=16 xoffset=1 yoffset=3 xadvance=13 page=0 chnl=0\n" + +"char id=73 x=172 y=38 width=8 height=16 xoffset=2 yoffset=3 xadvance=6 page=0 chnl=0\n" + +"char id=74 x=34 y=128 width=12 height=16 xoffset=1 yoffset=3 xadvance=9 page=0 chnl=0\n" + +"char id=75 x=158 y=2 width=16 height=16 xoffset=1 yoffset=3 xadvance=13 page=0 chnl=0\n" + +"char id=76 x=34 y=110 width=12 height=16 xoffset=1 yoffset=3 xadvance=11 page=0 chnl=0\n" + +"char id=77 x=84 y=2 width=18 height=16 xoffset=1 yoffset=3 xadvance=15 page=0 chnl=0\n" + +"char id=78 x=140 y=2 width=16 height=16 xoffset=1 yoffset=3 xadvance=13 page=0 chnl=0\n" + +"char id=79 x=122 y=2 width=16 height=16 xoffset=2 yoffset=3 xadvance=15 page=0 chnl=0\n" + +"char id=80 x=2 y=150 width=14 height=16 xoffset=1 yoffset=3 xadvance=13 page=0 chnl=0\n" + +"char id=81 x=66 y=2 width=16 height=18 xoffset=2 yoffset=2 xadvance=15 page=0 chnl=0\n" + +"char id=82 x=104 y=2 width=16 height=16 xoffset=1 yoffset=3 xadvance=13 page=0 chnl=0\n" + +"char id=83 x=2 y=132 width=14 height=16 xoffset=2 yoffset=3 xadvance=13 page=0 chnl=0\n" + +"char id=84 x=2 y=114 width=14 height=16 xoffset=3 yoffset=3 xadvance=11 page=0 chnl=0\n" + +"char id=85 x=2 y=96 width=14 height=16 xoffset=2 yoffset=3 xadvance=13 page=0 chnl=0\n" + +"char id=86 x=2 y=78 width=14 height=16 xoffset=3 yoffset=3 xadvance=13 page=0 chnl=0\n" + +"char id=87 x=24 y=2 width=20 height=16 xoffset=3 yoffset=3 xadvance=17 page=0 chnl=0\n" + +"char id=88 x=46 y=2 width=18 height=16 xoffset=0 yoffset=3 xadvance=13 page=0 chnl=0\n" + +"char id=89 x=2 y=60 width=14 height=16 xoffset=3 yoffset=3 xadvance=13 page=0 chnl=0\n" + +"char id=90 x=2 y=42 width=14 height=16 xoffset=1 yoffset=3 xadvance=11 page=0 chnl=0\n" + +"char id=91 x=18 y=168 width=10 height=20 xoffset=1 yoffset=2 xadvance=6 page=0 chnl=0\n" + +"char id=92 x=224 y=38 width=6 height=16 xoffset=2 yoffset=3 xadvance=6 page=0 chnl=0\n" + +"char id=93 x=18 y=146 width=10 height=20 xoffset=-1 yoffset=2 xadvance=6 page=0 chnl=0\n" + +"char id=94 x=212 y=38 width=10 height=10 xoffset=2 yoffset=2 xadvance=9 page=0 chnl=0\n" + +"char id=95 x=58 y=56 width=14 height=4 xoffset=-1 yoffset=18 xadvance=11 page=0 chnl=0\n" + +"char id=96 x=246 y=38 width=6 height=6 xoffset=3 yoffset=2 xadvance=6 page=0 chnl=0\n" + +"char id=97 x=184 y=24 width=12 height=12 xoffset=1 yoffset=7 xadvance=11 page=0 chnl=0\n" + +"char id=98 x=34 y=92 width=12 height=16 xoffset=1 yoffset=3 xadvance=11 page=0 chnl=0\n" + +"char id=99 x=170 y=24 width=12 height=12 xoffset=2 yoffset=7 xadvance=9 page=0 chnl=0\n" + +"char id=100 x=34 y=74 width=12 height=16 xoffset=1 yoffset=3 xadvance=11 page=0 chnl=0\n" + +"char id=101 x=156 y=24 width=12 height=12 xoffset=1 yoffset=7 xadvance=11 page=0 chnl=0\n" + +"char id=102 x=118 y=38 width=10 height=16 xoffset=1 yoffset=3 xadvance=6 page=0 chnl=0\n" + +"char id=103 x=34 y=56 width=12 height=16 xoffset=1 yoffset=6 xadvance=11 page=0 chnl=0\n" + +"char id=104 x=34 y=38 width=12 height=16 xoffset=1 yoffset=3 xadvance=11 page=0 chnl=0\n" + +"char id=105 x=162 y=38 width=8 height=16 xoffset=1 yoffset=3 xadvance=4 page=0 chnl=0\n" + +"char id=106 x=18 y=124 width=10 height=20 xoffset=-2 yoffset=2 xadvance=4 page=0 chnl=0\n" + +"char id=107 x=18 y=226 width=12 height=16 xoffset=1 yoffset=3 xadvance=9 page=0 chnl=0\n" + +"char id=108 x=152 y=38 width=8 height=16 xoffset=1 yoffset=3 xadvance=4 page=0 chnl=0\n" + +"char id=109 x=34 y=24 width=18 height=12 xoffset=1 yoffset=7 xadvance=15 page=0 chnl=0\n" + +"char id=110 x=142 y=24 width=12 height=12 xoffset=1 yoffset=7 xadvance=11 page=0 chnl=0\n" + +"char id=111 x=128 y=24 width=12 height=12 xoffset=1 yoffset=7 xadvance=11 page=0 chnl=0\n" + +"char id=112 x=18 y=208 width=12 height=16 xoffset=0 yoffset=6 xadvance=11 page=0 chnl=0\n" + +"char id=113 x=18 y=190 width=12 height=16 xoffset=1 yoffset=6 xadvance=11 page=0 chnl=0\n" + +"char id=114 x=240 y=24 width=10 height=12 xoffset=1 yoffset=7 xadvance=6 page=0 chnl=0\n" + +"char id=115 x=114 y=24 width=12 height=12 xoffset=1 yoffset=7 xadvance=9 page=0 chnl=0\n" + +"char id=116 x=142 y=38 width=8 height=16 xoffset=2 yoffset=3 xadvance=6 page=0 chnl=0\n" + +"char id=117 x=100 y=24 width=12 height=12 xoffset=2 yoffset=7 xadvance=11 page=0 chnl=0\n" + +"char id=118 x=86 y=24 width=12 height=12 xoffset=2 yoffset=7 xadvance=9 page=0 chnl=0\n" + +"char id=119 x=54 y=24 width=16 height=12 xoffset=2 yoffset=7 xadvance=13 page=0 chnl=0\n" + +"char id=120 x=72 y=24 width=12 height=12 xoffset=0 yoffset=7 xadvance=9 page=0 chnl=0\n" + +"char id=121 x=2 y=24 width=14 height=16 xoffset=0 yoffset=6 xadvance=9 page=0 chnl=0\n" + +"char id=122 x=2 y=240 width=12 height=12 xoffset=1 yoffset=7 xadvance=9 page=0 chnl=0\n" + +"char id=123 x=18 y=102 width=10 height=20 xoffset=1 yoffset=2 xadvance=7 page=0 chnl=0\n" + +"char id=124 x=248 y=2 width=4 height=20 xoffset=2 yoffset=2 xadvance=5 page=0 chnl=0\n" + +"char id=125 x=18 y=80 width=10 height=20 xoffset=-1 yoffset=2 xadvance=7 page=0 chnl=0\n" + +"char id=126 x=232 y=38 width=12 height=6 xoffset=2 yoffset=8 xadvance=11 page=0 chnl=0\n" + +"kernings count=38\n" + +"kerning first=84 second=97 amount=-1\n" + +"kerning first=84 second=99 amount=-1\n" + +"kerning first=80 second=65 amount=-1\n" + +"kerning first=89 second=111 amount=-1\n" + +"kerning first=84 second=111 amount=-1\n" + +"kerning first=89 second=112 amount=-1\n" + +"kerning first=84 second=65 amount=-1\n" + +"kerning first=80 second=44 amount=-2\n" + +"kerning first=89 second=113 amount=-1\n" + +"kerning first=76 second=84 amount=-1\n" + +"kerning first=80 second=46 amount=-2\n" + +"kerning first=76 second=86 amount=-1\n" + +"kerning first=76 second=87 amount=-1\n" + +"kerning first=84 second=58 amount=-1\n" + +"kerning first=84 second=44 amount=-1\n" + +"kerning first=84 second=115 amount=-1\n" + +"kerning first=76 second=89 amount=-1\n" + +"kerning first=84 second=46 amount=-1\n" + +"kerning first=89 second=97 amount=-1\n" + +"kerning first=89 second=101 amount=-1\n" + +"kerning first=49 second=49 amount=-1\n" + +"kerning first=84 second=101 amount=-1\n" + +"kerning first=86 second=97 amount=-1\n" + +"kerning first=89 second=65 amount=-1\n" + +"kerning first=118 second=44 amount=-1\n" + +"kerning first=65 second=84 amount=-1\n" + +"kerning first=65 second=86 amount=-1\n" + +"kerning first=118 second=46 amount=-1\n" + +"kerning first=65 second=89 amount=-1\n" + +"kerning first=89 second=44 amount=-2\n" + +"kerning first=86 second=65 amount=-1\n" + +"kerning first=89 second=46 amount=-2\n" + +"kerning first=121 second=44 amount=-1\n" + +"kerning first=70 second=44 amount=-1\n" + +"kerning first=86 second=44 amount=-1\n" + +"kerning first=70 second=46 amount=-1\n" + +"kerning first=121 second=46 amount=-1\n" + +"kerning first=86 second=46 amount=-1"; + +describe("me.BitmapFontData", function () { + describe("parse", function () { + var bitmapFontData = null; + beforeEach(function () { + bitmapFontData = new me.BitmapFontData(); + }); + + it("should have 95 glyphs", function () { + bitmapFontData.parse(bitmapFontDataFixture); + expect(Object.keys(bitmapFontData.glyphs).length).toEqual(95); + }); + + it("glyph by character 33 should have set data", function () { + bitmapFontData.parse(bitmapFontDataFixture); + var glyph = bitmapFontData.glyphs[33]; + expect(glyph.src.x).toEqual(192); + expect(glyph.src.y).toEqual(38); + expect(glyph.width).toEqual(8); + expect(glyph.height).toEqual(16); + expect(glyph.offset.x).toEqual(2); + expect(glyph.offset.y).toEqual(3); + expect(glyph.xadvance).toEqual(6); + }); + + it("glyph by character 70 should have kerning data", function () { + bitmapFontData.parse(bitmapFontDataFixture); + var glyph = bitmapFontData.glyphs[70]; + expect(glyph.getKerning(44)).toEqual(-1); + }); + }); +}); \ No newline at end of file From 218ceafff5d9042d3bdcf950c38811855805aab5 Mon Sep 17 00:00:00 2001 From: Jay Oster Date: Sat, 21 May 2016 17:12:51 -0700 Subject: [PATCH 021/177] Update jasmine path in tests/index.html --- tests/index.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/index.html b/tests/index.html index a475d884c8..3bf9c5fd74 100644 --- a/tests/index.html +++ b/tests/index.html @@ -4,10 +4,10 @@ melonJS unit-tests - - - - + + + + From cc00ea07e7676001b4980a0ad3415ddcca655bcb Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Sat, 21 May 2016 20:59:12 -0400 Subject: [PATCH 022/177] Added bitmap font scale. --- examples/font_test/main.js | 8 ++++---- src/font/bitmapfont.js | 15 +++++++++------ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/examples/font_test/main.js b/examples/font_test/main.js index ee3ea4ef64..78f68cb260 100644 --- a/examples/font_test/main.js +++ b/examples/font_test/main.js @@ -155,8 +155,8 @@ var FontTest = me.Renderable.extend ({ this.font.textAlign = "right"; this.font.draw(renderer, text, 200, 300); - this.drawBitmapFont(renderer, this.bFont, 0, baselines); - this.drawBitmapFont(renderer, this.fancyBFont, 300, baselines); + this.drawBitmapFont(renderer, this.bFont, 0, baselines, 1.0); + this.drawBitmapFont(renderer, this.fancyBFont, 300, baselines, 1.2); // restore default alignement/baseline this.font.textAlign = "left"; @@ -167,11 +167,11 @@ var FontTest = me.Renderable.extend ({ this.fancyBFont.textBaseline = "top"; }, - drawBitmapFont: function (renderer, font, yOffset, baselines) { + drawBitmapFont: function (renderer, font, yOffset, baselines, scale) { // bFont test font.textAlign = "center"; var text = "THIS IS A MULTILINE\n BITMAP FONT WITH MELONJS\nAND IT WORKS"; - font.resize(2); + font.resize(scale); font.draw(renderer, text + "\n" + text, 400, 230 + yOffset); // bFont test diff --git a/src/font/bitmapfont.js b/src/font/bitmapfont.js index 43dfd23e89..72c52eb8d0 100644 --- a/src/font/bitmapfont.js +++ b/src/font/bitmapfont.js @@ -104,7 +104,7 @@ for (var i = 0; i < characters.length; i++) { var ch = characters[i].charCodeAt(0); var glyph = this.bitmapFontData.glyphs[ch]; - width += glyph.xadvance + (lastGlyph ? lastGlyph.getKerning(ch) : 0); + width += (glyph.xadvance + (lastGlyph ? lastGlyph.getKerning(ch) : 0) * this.fontScale.x); } return width; @@ -128,7 +128,7 @@ height += stringHeight; } - return {width: width, height: height}; + return {width: width, height: height * this.fontScale.y}; }, /** @@ -144,7 +144,7 @@ draw : function (renderer, text, x, y) { var strings = ("" + text).split("\n"); var lX = x; - var stringHeight = this.bitmapFontData.capHeight * this.lineHeight; + var stringHeight = this.bitmapFontData.capHeight * this.lineHeight * this.fontScale.y; // save the previous global alpha value var _alpha = renderer.globalAlpha(); @@ -186,6 +186,9 @@ break; } + // x *= this.fontScale.x; + // y *= this.fontScale.y; + // draw the string var lastGlyph = null; for (var c = 0, len = string.length; c < len; c++) { @@ -197,9 +200,9 @@ renderer.drawImage(this.fontImage, glyph.src.x, glyph.src.y, glyph.width, glyph.height, - ~~x, ~~y + glyph.offset.y, - glyph.width, glyph.height); - x += glyph.xadvance + (lastGlyph ? lastGlyph.getKerning(ch) : 0); + ~~x, ~~y + glyph.offset.y * this.fontScale.y, + glyph.width * this.fontScale.x, glyph.height * this.fontScale.y); + x += (glyph.xadvance + (lastGlyph ? lastGlyph.getKerning(ch) : 0)) * this.fontScale.x; lastGlyph = glyph; } // increment line From 8e10fce75d0d916f0246867d7a4765d85ba8535a Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Sat, 21 May 2016 21:07:50 -0400 Subject: [PATCH 023/177] clean up unneeded props --- src/font/bitmapfontdata.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/font/bitmapfontdata.js b/src/font/bitmapfontdata.js index c2c16b34a1..56263ff434 100644 --- a/src/font/bitmapfontdata.js +++ b/src/font/bitmapfontdata.js @@ -16,15 +16,10 @@ me.BitmapFontData = me.Object.extend({ // The distance from the top of most uppercase characters to the baseline. Since the drawing position is the cap height of // the first line, the cap height can be used to get the location of the baseline. this.capHeight = 1; - // The distance from the cap height to the top of the tallest glyph. - this.ascent = 0; // The distance from the bottom of the glyph that extends the lowest to the baseline. This number is negative. this.descent = 0; this.down = 0; this.scale = new me.Vector2d(); - // The amount to add to the glyph X position when drawing a cursor between glyphs. This field is not set by the BMFont - // file, it needs to be set manually depending on how the glyphs are rendered on the backing textures. - this.cursorX = 0; /** * The map of glyphs, each key is a char code. @@ -34,10 +29,13 @@ me.BitmapFontData = me.Object.extend({ */ this.glyphs = {}; - // The width of the space character. + /** + * The width of the space glyph + * @name spaceWidth + * @property + * @memberOf me.BitmapFontData + */ this.spaceWidth = 0; - // The x-height, which is the distance from the top of most lowercase characters to the baseline. - this.xHeight = 1; this.xChars = ["x", "e", "a", "o", "n", "s", "r", "c", "u", "m", "v", "w", "z"]; this.capChars = ["M", "N", "B", "D", "C", "E", "F", "K", "A", "G", "H", "I", "J", "L", "O", "P", "Q", "R", "S", @@ -180,7 +178,6 @@ me.BitmapFontData = me.Object.extend({ this.capHeight -= padY; - this.ascent = this.baseLine - this.capHeight; this.down = -this.lineHeight; }, From d2ba2dd3f4ff0ed431f7a92df6df0ce0ee405843 Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Sat, 21 May 2016 21:24:35 -0400 Subject: [PATCH 024/177] improved docs. --- src/font/bitmapfont.js | 12 ++++++++---- src/font/bitmapfontdata.js | 37 +++++++++++++++++++++++++++---------- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/font/bitmapfont.js b/src/font/bitmapfont.js index 72c52eb8d0..db9fdf423d 100644 --- a/src/font/bitmapfont.js +++ b/src/font/bitmapfont.js @@ -35,6 +35,12 @@ var fontData = me.loader.getBinary(fontName); this.fontImage = me.loader.getImage(fontName); + /** + * The instance of me.BitmapFontData + * @type {me.BitmapFontData} + * @name bitmapFontData + * @memberOf me.BitmapFont + */ this.bitmapFontData = new me.BitmapFontData(); this.bitmapFontData.parse(fontData); this.fontScale = me.pool.pull("me.Vector2d", 1, 1); @@ -47,9 +53,7 @@ throw "Font image for font name: " + fontName + " not found"; } - // #char per row this.charCount = 0; - // font name and type this._super(me.Renderable, "init", [0, 0, 0, 0, 0, 0]); // set a default alignement @@ -95,7 +99,7 @@ * @memberOf me.BitmapFont * @function * @param {String} text - * @return {Number} the calculated width + * @returns {Number} the calculated width */ measureTextWidth : function(text) { var characters = text.split(""); @@ -116,7 +120,7 @@ * @memberOf me.BitmapFont * @function * @param {String} text - * @return {Object} an object with two properties: `width` and `height`, defining the output dimensions + * @returns {Object} an object with two properties: `width` and `height`, defining the output dimensions */ measureText : function (text) { var strings = ("" + text).split("\n"); diff --git a/src/font/bitmapfontdata.js b/src/font/bitmapfontdata.js index 56263ff434..fd06a137be 100644 --- a/src/font/bitmapfontdata.js +++ b/src/font/bitmapfontdata.js @@ -1,6 +1,5 @@ /** * Class for storing relevant data from the font file. - * @private * @class me.BitmapFontData * @memberOf me * @constructor @@ -11,7 +10,6 @@ me.BitmapFontData = me.Object.extend({ this.padRight = 0; this.padBottom = 0; this.padLeft = 0; - // The distance from one line of text to the next. To set this value, use {@link #setLineHeight(float)}. this.lineHeight = 0; // The distance from the top of most uppercase characters to the baseline. Since the drawing position is the cap height of // the first line, the cap height can be used to get the location of the baseline. @@ -24,7 +22,7 @@ me.BitmapFontData = me.Object.extend({ /** * The map of glyphs, each key is a char code. * @name glyphs - * @property + * @type {Object} * @memberOf me.BitmapFontData */ this.glyphs = {}; @@ -32,7 +30,7 @@ me.BitmapFontData = me.Object.extend({ /** * The width of the space glyph * @name spaceWidth - * @property + * @type {Number} * @memberOf me.BitmapFontData */ this.spaceWidth = 0; @@ -42,6 +40,13 @@ me.BitmapFontData = me.Object.extend({ "T", "U", "V", "W", "X", "Y", "Z"]; }, + /** + * Creates a glyph to use for the space character + * @private + * @name _createSpaceGlyph + * @memberOf me.BitmapFontData + * @function + */ _createSpaceGlyph: function () { var spaceCharCode = " ".charCodeAt(0); if (!this.glyphs[spaceCharCode]) { @@ -59,10 +64,27 @@ me.BitmapFontData = me.Object.extend({ } }, + /** + * Gets the first glyph in the map + * @private + * @name _getFirstGlyph + * @memberOf me.BitmapFontData + * @function + * @returns {me.Glyph} + */ _getFirstGlyph: function () { return this.glyphs[Object.keys(this.glyphs)[0]]; }, + /** + * Gets the value from a string of pairs. For example: one=1 two=2 something=hi. Can accept the regex of /one={d}/ + * and returns the value of d + * @private + * @name _getValueFromPair + * @memberOf me.BitmapFontData + * @function + * @returns {String} + */ _getValueFromPair: function (string, pattern) { var value = string.match(pattern); if (!value) { @@ -179,10 +201,5 @@ me.BitmapFontData = me.Object.extend({ this.capHeight -= padY; this.down = -this.lineHeight; - }, - - setLineHeight: function (height) { - this.lineHeight = height * this.scale.y; - this.down = -this.lineHeight; - }, + } }); \ No newline at end of file From abc3053ed77c959db50d4a7b3138626fdd715f56 Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Sat, 21 May 2016 21:27:32 -0400 Subject: [PATCH 025/177] example in docs fixed. --- src/font/bitmapfont.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/font/bitmapfont.js b/src/font/bitmapfont.js index db9fdf423d..63ce2c9658 100644 --- a/src/font/bitmapfont.js +++ b/src/font/bitmapfont.js @@ -10,11 +10,6 @@ (function () { /** * a bitmap font object - * Use me.loader.preload or me.loader.load to load assets - * me.loader.preload([ - * { name: "arial", type: "binary" src: "data/font/arial.fnt" }, - * { name: "arial", type: "image" src: "data/font/arial.png" }, - * ]) * @class * @extends me.Renderable * @memberOf me @@ -23,6 +18,16 @@ * @param {Number} [scale=1.0] * @param {String} [textAlign=left] * @param {String} [textBaseline=top] + * @example + * // Use me.loader.preload or me.loader.load to load assets + * me.loader.preload([ + * { name: "arial", type: "binary" src: "data/font/arial.fnt" }, + * { name: "arial", type: "image" src: "data/font/arial.png" }, + * ]) + * // Then create an instance of your bitmap font: + * var myFont = new me.BitmapFont("arial"); + * // And draw it inside your Renderable, just like me.Font + * myFont.draw(renderer, "Hello!", 0, 0); */ me.BitmapFont = me.Renderable.extend( /** @scope me.BitmapFont.prototype */ { From 7757886c6c8958f0efa41899a95f2bef112aeb61 Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Wed, 25 May 2016 22:45:14 -0400 Subject: [PATCH 026/177] changed constructor to accept data from loader. --- examples/font_test/main.js | 4 ++-- src/font/bitmapfont.js | 20 +++++--------------- src/font/bitmapfontdata.js | 4 +++- tests/spec/bitmapfontdata-spec.js | 5 +---- 4 files changed, 11 insertions(+), 22 deletions(-) diff --git a/examples/font_test/main.js b/examples/font_test/main.js index 78f68cb260..423137e694 100644 --- a/examples/font_test/main.js +++ b/examples/font_test/main.js @@ -71,11 +71,11 @@ var FontTest = me.Renderable.extend ({ // arial font this.font = new me.Font("Arial", 8, this.color); // bitmap font - this.bFont = new me.BitmapFont("xolo12"); + this.bFont = new me.BitmapFont(new me.BitmapFontData(me.loader.getBinary('xolo12')), me.loader.getImage('xolo12')); this.bFont.name = 'xolo'; this.name = "FontTest"; - this.fancyBFont = new me.BitmapFont("arialfancy"); + this.fancyBFont = new me.BitmapFont(new me.BitmapFontData(me.loader.getBinary('arialfancy')), me.loader.getImage('arialfancy')); this.fancyBFont.name = 'arial'; }, diff --git a/src/font/bitmapfont.js b/src/font/bitmapfont.js index 63ce2c9658..dd49151c86 100644 --- a/src/font/bitmapfont.js +++ b/src/font/bitmapfont.js @@ -14,7 +14,7 @@ * @extends me.Renderable * @memberOf me * @constructor - * @param {String} font font name + * @param {Image} the image object for the font. Should be retrieved from the loader * @param {Number} [scale=1.0] * @param {String} [textAlign=left] * @param {String} [textBaseline=top] @@ -25,20 +25,19 @@ * { name: "arial", type: "image" src: "data/font/arial.png" }, * ]) * // Then create an instance of your bitmap font: - * var myFont = new me.BitmapFont("arial"); + * var myFont = new me.BitmapFont(new me.BitmapFontData(me.loader.getBinary("arial")), me.loader.getImage("arial")); * // And draw it inside your Renderable, just like me.Font * myFont.draw(renderer, "Hello!", 0, 0); */ me.BitmapFont = me.Renderable.extend( /** @scope me.BitmapFont.prototype */ { /** @ignore */ - init : function (fontName, scale, textAlign, textBaseline) { + init : function (data, fontImage, scale, textAlign, textBaseline) { /** @ignore */ // scaled font size; this.sSize = me.pool.pull("me.Vector2d", 0, 0); - var fontData = me.loader.getBinary(fontName); - this.fontImage = me.loader.getImage(fontName); + this.fontImage = fontImage; /** * The instance of me.BitmapFontData @@ -46,18 +45,9 @@ * @name bitmapFontData * @memberOf me.BitmapFont */ - this.bitmapFontData = new me.BitmapFontData(); - this.bitmapFontData.parse(fontData); + this.bitmapFontData = data; this.fontScale = me.pool.pull("me.Vector2d", 1, 1); - if (!fontData) { - throw "Font data for font name: " + fontName + " not found"; - } - - if (!this.fontImage) { - throw "Font image for font name: " + fontName + " not found"; - } - this.charCount = 0; this._super(me.Renderable, "init", [0, 0, 0, 0, 0, 0]); diff --git a/src/font/bitmapfontdata.js b/src/font/bitmapfontdata.js index fd06a137be..f4f5b53682 100644 --- a/src/font/bitmapfontdata.js +++ b/src/font/bitmapfontdata.js @@ -2,10 +2,11 @@ * Class for storing relevant data from the font file. * @class me.BitmapFontData * @memberOf me + * @param data {String} - The bitmap font data pulled from the resource loader using me.loader.getBinary() * @constructor */ me.BitmapFontData = me.Object.extend({ - init: function () { + init: function (data) { this.padTop = 0; this.padRight = 0; this.padBottom = 0; @@ -38,6 +39,7 @@ me.BitmapFontData = me.Object.extend({ this.xChars = ["x", "e", "a", "o", "n", "s", "r", "c", "u", "m", "v", "w", "z"]; this.capChars = ["M", "N", "B", "D", "C", "E", "F", "K", "A", "G", "H", "I", "J", "L", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]; + this.parse(data); }, /** diff --git a/tests/spec/bitmapfontdata-spec.js b/tests/spec/bitmapfontdata-spec.js index 8e8a3632eb..5437bb21f5 100644 --- a/tests/spec/bitmapfontdata-spec.js +++ b/tests/spec/bitmapfontdata-spec.js @@ -141,16 +141,14 @@ describe("me.BitmapFontData", function () { describe("parse", function () { var bitmapFontData = null; beforeEach(function () { - bitmapFontData = new me.BitmapFontData(); + bitmapFontData = new me.BitmapFontData(bitmapFontDataFixture); }); it("should have 95 glyphs", function () { - bitmapFontData.parse(bitmapFontDataFixture); expect(Object.keys(bitmapFontData.glyphs).length).toEqual(95); }); it("glyph by character 33 should have set data", function () { - bitmapFontData.parse(bitmapFontDataFixture); var glyph = bitmapFontData.glyphs[33]; expect(glyph.src.x).toEqual(192); expect(glyph.src.y).toEqual(38); @@ -162,7 +160,6 @@ describe("me.BitmapFontData", function () { }); it("glyph by character 70 should have kerning data", function () { - bitmapFontData.parse(bitmapFontDataFixture); var glyph = bitmapFontData.glyphs[70]; expect(glyph.getKerning(44)).toEqual(-1); }); From 8311f97fe7e1cdc918c28363a686b7e996a52f46 Mon Sep 17 00:00:00 2001 From: obiot Date: Thu, 26 May 2016 12:36:58 +0800 Subject: [PATCH 027/177] few improvements to the `me.Matrix2d` object added some more helper function, and renamed others to better match a more "standard" naming --- src/math/matrix2.js | 75 +++++++++++++++++++++++++++-------- src/video/webgl/compositor.js | 12 +++--- 2 files changed, 65 insertions(+), 22 deletions(-) diff --git a/src/math/matrix2.js b/src/math/matrix2.js index d1c8e5dfcb..bc5043c619 100644 --- a/src/math/matrix2.js +++ b/src/math/matrix2.js @@ -26,7 +26,7 @@ this.copy(a); } else if (arguments.length === 9) { - this.set(a, b, c, d, e, f, g, h, i); + this.setTransform(a, b, c, d, e, f, g, h, i); } else { this.identity(); @@ -43,7 +43,7 @@ * @return {me.Matrix2d} Reference to this object for method chaining */ identity : function () { - this.set( + this.setTransform( 1, 0, 0, 0, 1, 0, 0, 0, 1 @@ -54,7 +54,7 @@ /** * set the matrix to the specified value - * @name set + * @name setTransform * @memberOf me.Matrix2d * @function * @param {Number} aX @@ -68,7 +68,7 @@ * @param {Number} cW * @return {me.Matrix2d} Reference to this object for method chaining */ - set : function () { + setTransform : function () { var a = this.val; a[0] = arguments[0]; @@ -89,11 +89,11 @@ * @name copy * @memberOf me.Matrix2d * @function - * @param {me.Matrix2d} b the matrix object to copy from + * @param {me.Matrix2d} m the matrix object to copy from * @return {me.Matrix2d} Reference to this object for method chaining */ copy : function (b) { - this.val.set(b.val); + this.val.setTransform(b.val); return this; }, @@ -129,11 +129,16 @@ return this; }, - /** - * Multiply this matrix into a vector - * @ignore - */ - vectorMultiply : function (v) { + + /** + * Transforms the given vector according to this matrix. + * @name transformVector + * @memberOf me.Matrix2d + * @function + * @param {me.Vector2d} vector the vector object to be transformed + * @return {me.Vector2d} result vector object. Useful for chaining method calls. + **/ + transformVector : function (v) { var a = this.val, x = v.x, y = v.y; @@ -154,16 +159,54 @@ * @return {me.Matrix2d} Reference to this object for method chaining */ scale : function (x, y) { - var a = this.val; + var a = this.val, + _x = x, + _y = typeof(y) === "undefined" ? _x : y; - a[0] *= x; - a[1] *= x; - a[3] *= y; - a[4] *= y; + a[0] *= _x; + a[1] *= _x; + a[3] *= _y; + a[4] *= _y; return this; }, + /** + * adds a 2D scaling transformation. + * @name scaleV + * @memberOf me.Matrix2d + * @function + * @param {me.Vector2d} vector scaling vector + * @return {me.Matrix2d} Reference to this object for method chaining + */ + scaleV : function (v) { + return this.scale(v.x, v.y); + }, + + /** + * specifies a 2D scale operation using the [sx, 1] scaling vector + * @name scaleX + * @memberOf me.Matrix2d + * @function + * @param {Number} x x scaling vector + * @return {me.Matrix2d} Reference to this object for method chaining + */ + scaleX : function (x) { + return this.scale(x, 1); + }, + + /** + * specifies a 2D scale operation using the [1,sy] scaling vector + * @name scaleY + * @memberOf me.Matrix2d + * @function + * @param {Number} y y scaling vector + * @return {me.Matrix2d} Reference to this object for method chaining + */ + scaleY : function (y) { + return this.scale(1, y); + }, + /** * rotate the matrix (counter-clockwise) by the specified angle (in radians). * @name rotate diff --git a/src/video/webgl/compositor.js b/src/video/webgl/compositor.js index b27b3dc79a..a55a880088 100644 --- a/src/video/webgl/compositor.js +++ b/src/video/webgl/compositor.js @@ -201,7 +201,7 @@ setProjection : function (w, h) { this.flush(); this.gl.viewport(0, 0, w, h); - this.uMatrix.set( + this.uMatrix.setTransform( 2 / w, 0, 0, 0, -2 / h, 0, -1, 1, 1 @@ -331,10 +331,10 @@ v3 = this.v[3].set(x + w, y + h); if (!m.isIdentity()) { - m.vectorMultiply(v0); - m.vectorMultiply(v1); - m.vectorMultiply(v2); - m.vectorMultiply(v3); + m.transformVector(v0); + m.transformVector(v1); + m.transformVector(v2); + m.transformVector(v3); } // Array index computation @@ -445,7 +445,7 @@ var j = 0; for (var i = 0; i < points.length; i++) { if (!this.matrix.isIdentity()) { - this.matrix.vectorMultiply(points[i]); + this.matrix.transformVector(points[i]); } this.stream[j++] = points[i].x; this.stream[j++] = points[i].y; From ab39f6f403bbf2c03229f9f414070af45047db57 Mon Sep 17 00:00:00 2001 From: obiot Date: Thu, 26 May 2016 13:34:43 +0800 Subject: [PATCH 028/177] fixes a regression in the particle emiter following this commit : https://github.com/melonjs/melonJS/commit/8311f97fe7e1cdc918c28363a686b7e996a52f46 --- src/particles/particle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/particles/particle.js b/src/particles/particle.js index 2be553c8bf..efe8a6788c 100644 --- a/src/particles/particle.js +++ b/src/particles/particle.js @@ -124,7 +124,7 @@ this.pos.y += this.vel.y * skew; // Update particle transform - this.transform.set( + this.transform.setTransform( scale, 0, 0, 0, scale, 0, ~~this.pos.x, ~~this.pos.y, 1 From eb7cf7319423376b37bf9f386781ea68da7b3abc Mon Sep 17 00:00:00 2001 From: obiot Date: Thu, 26 May 2016 13:47:56 +0800 Subject: [PATCH 029/177] [#335] me.Renderable now has a me.Matrix2d property that hold all the object (renderable) transformations this first commit provides a first backward compatible implementation. As so far, I saw no regression in all the code I could test. --- src/renderable/container.js | 9 ----- src/renderable/renderable.js | 16 ++++++++ src/renderable/sprite.js | 76 ++++++++++++------------------------ 3 files changed, 42 insertions(+), 59 deletions(-) diff --git a/src/renderable/container.js b/src/renderable/container.js index 4539a79a5a..4a24c212ed 100644 --- a/src/renderable/container.js +++ b/src/renderable/container.js @@ -43,15 +43,6 @@ // TODO; container do not have a physic body // ADD child container child one by one to the quadtree? - /** - * the container default transformation matrix - * @public - * @type me.Matrix2d - * @name transform - * @memberOf me.Container - */ - this.transform = new me.Matrix2d(); - /** * whether the container is the root of the scene * @private diff --git a/src/renderable/renderable.js b/src/renderable/renderable.js index 69ca23e54a..d7dbc7f8ed 100644 --- a/src/renderable/renderable.js +++ b/src/renderable/renderable.js @@ -30,6 +30,15 @@ */ this.isRenderable = true; + /** + * the renderable default transformation matrix + * @public + * @type me.Matrix2d + * @name transform + * @memberOf me.Renderable + */ + this.transform = undefined; + /** * (G)ame (U)nique (Id)entifier"
* a GUID will be allocated for any renderable object added
@@ -165,6 +174,13 @@ this.pos = new me.ObservableVector3d(x, y, 0, { onUpdate: this.updateBoundsPos.bind(this) }); } + // create or reset the transformation matrix + if (this.transform) { + this.transform.identity(); + } else { + this.transform = new me.Matrix2d(); + } + this._width = width; this._height = height; diff --git a/src/renderable/sprite.js b/src/renderable/sprite.js index eaea106551..e08f292740 100644 --- a/src/renderable/sprite.js +++ b/src/renderable/sprite.js @@ -35,16 +35,6 @@ * @ignore */ init : function (x, y, settings) { - - /** - * private/internal scale factor - * @ignore - */ - this._scale = new me.Vector2d(1, 1); - - // if true, image flipping/scaling is needed - this.scaleFlag = false; - // just to keep track of when we flip this.lastflipX = false; this.lastflipY = false; @@ -62,16 +52,6 @@ */ this.offset = new me.Vector2d(); - /** - * Set the angle (in Radians) of a sprite to rotate it
- * WARNING: rotating sprites decreases performance with Canvas Renderer - * @public - * @type Number - * @default 0 - * @name me.Sprite#angle - */ - this.angle = settings.rotation || 0; - /** * Source rotation angle for pre-rotating the source image
* Commonly used for TexturePacker @@ -123,6 +103,12 @@ settings.frameheight || this.image.height ]); + // set the default rotation angle is defined in the settings + // * WARNING: rotating sprites decreases performance with Canvas Renderer + if (typeof (settings.rotation) !== "undefined") { + this.transform.rotate(settings.rotation); + } + // update anchorPoint if (settings.anchorPoint) { this.anchorPoint.set(settings.anchorPoint.x, settings.anchorPoint.y); @@ -175,14 +161,13 @@ * @param {Boolean} flip enable/disable flip */ flipX : function (flip) { + console.warn("Deprecated: me.Sprite.flipX"); if (flip !== this.lastflipX) { this.lastflipX = flip; - // invert the scale.x value - this._scale.x = -this._scale.x; + this.transform.scaleX(-1); - // set the scaleFlag - this.scaleFlag = this._scale.x !== 1.0 || this._scale.y !== 1.0; + //console.log(this.transform._scale.x); } }, @@ -194,14 +179,11 @@ * @param {Boolean} flip enable/disable flip */ flipY : function (flip) { + console.warn("Deprecated: me.Sprite.flipY"); if (flip !== this.lastflipY) { this.lastflipY = flip; - // invert the scale.x value - this._scale.y = -this._scale.y; - - // set the scaleFlag - this.scaleFlag = this._scale.x !== 1.0 || this._scale.y !== 1.0; + this.transform.scaleY(-1); } }, @@ -210,24 +192,17 @@ * @name scale * @memberOf me.Sprite * @function - * @param {Number} ratioX x scaling ratio - * @param {Number} ratioY y scaling ratio + * @param {Number} x x scaling ratio + * @param {Number} y y scaling ratio */ - scale : function (ratioX, ratioY) { - var x = ratioX; - var y = typeof(ratioY) === "undefined" ? ratioX : ratioY; - if (x > 0) { - this._scale.x = this._scale.x < 0.0 ? -x : x; - } - if (y > 0) { - this._scale.y = this._scale.y < 0.0 ? -y : y; - } + scale : function (x, y) { + console.warn("Deprecated: me.Sprite.scale"); + // set the scaleFlag - this.scaleFlag = this._scale.x !== 1.0 || this._scale.y !== 1.0; + this.transform.scale(x, y); // resize the bounding box this.resizeBounds(this.width * x, this.height * y); - }, /** @@ -238,6 +213,7 @@ * @param {me.Vector2d} vector ratio */ scaleV : function (ratio) { + console.warn("Deprecated: me.Sprite.scaleV"); this.scale(ratio.x, ratio.y); }, @@ -302,19 +278,19 @@ xpos -= ax; ypos -= ay; - if ((this.scaleFlag) || (this.angle !== 0) || (this._sourceAngle !== 0)) { + var _isTransformed = !this.transform.isIdentity(); + + if (_isTransformed || (this._sourceAngle !== 0)) { + // translate to the defined anchor point xpos += ax; ypos += ay; renderer.translate(xpos, ypos); - // rotate - if (this.angle !== 0) { - renderer.rotate(this.angle); - } - // scale - if (this.scaleFlag) { - renderer.scale(this._scale.x, this._scale.y); + + if (_isTransformed) { + renderer.transform(this.transform); } + // remove image's TexturePacker/ShoeBox rotation if (this._sourceAngle !== 0) { renderer.translate(-(xpos+ax), -(ypos+ay)); From f23bd751ad36a7c9765e6168578c82ccc8d8572d Mon Sep 17 00:00:00 2001 From: obiot Date: Thu, 26 May 2016 16:53:31 +0800 Subject: [PATCH 030/177] Fix the TexturePacker example, that was using a local copy of the library ! and yes changes, in ticket #335 are still working :) --- examples/texturepacker/index.html | 7 +- examples/texturepacker/lib/melonJS.js | 26930 ---------------- .../lib/plugins/debug/debugPanel.js | 512 - 3 files changed, 3 insertions(+), 27446 deletions(-) delete mode 100644 examples/texturepacker/lib/melonJS.js delete mode 100644 examples/texturepacker/lib/plugins/debug/debugPanel.js diff --git a/examples/texturepacker/index.html b/examples/texturepacker/index.html index 5d5e6aa529..30c2cfc424 100644 --- a/examples/texturepacker/index.html +++ b/examples/texturepacker/index.html @@ -18,11 +18,10 @@
- - + - + @@ -30,7 +29,7 @@ - + + + diff --git a/tests/spec/container-spec.js b/tests/spec/container-spec.js index 7a3f71ac06..0d2800d61c 100644 --- a/tests/spec/container-spec.js +++ b/tests/spec/container-spec.js @@ -4,7 +4,7 @@ describe("me.Container", function () { describe("isAttachedToRoot", function () { describe("game world", function () { beforeEach(function () { - container = new me.Container(); + container = new me.Container(0, 0, 100, 100); container._root = true; }); @@ -12,6 +12,26 @@ describe("me.Container", function () { expect(container.isAttachedToRoot()).toEqual(true); }); + it("me.Container bounds return default assigned size", function () { + var bounds = container.getBounds(); + expect(bounds.pos.x).toEqual(0); + expect(bounds.pos.y).toEqual(0); + expect(bounds.width).toEqual(100); + expect(bounds.height).toEqual(100); + }); + + it("me.Container childbounds return the union of all child bounds", function () { + container.addChild(new me.Renderable(50, 50, 100, 100)); + container.addChild(new me.Renderable(100, 100, 100, 100)); + // update the child bounds + container.updateChildBounds(); + var bounds = container.childBounds; + expect(bounds.pos.x).toEqual(50); + expect(bounds.pos.y).toEqual(50); + expect(bounds.width).toEqual(150); + expect(bounds.height).toEqual(150); + }); + it("a new container should not be root", function () { var secondContainer = new me.Container(); expect(secondContainer.isAttachedToRoot()).toEqual(false); diff --git a/tests/spec/renderable-spec.js b/tests/spec/renderable-spec.js index d937a16ce9..76a177b39c 100644 --- a/tests/spec/renderable-spec.js +++ b/tests/spec/renderable-spec.js @@ -7,22 +7,22 @@ describe("me.Renderable", function () { it("setting x position changes x bounds", function () { renderable.pos.x = 10; - expect(renderable._bounds.pos.x).toEqual(10); + expect(renderable.getBounds().pos.x).toEqual(10); }); it("setting y position changes y bounds", function () { renderable.pos.y = 120; - expect(renderable._bounds.pos.y).toEqual(120); + expect(renderable.getBounds().pos.y).toEqual(120); }); it("resizing the renderable changes its bounds width", function () { renderable.resize(20, 20); - expect(renderable._bounds.width).toEqual(20); + expect(renderable.getBounds().width).toEqual(20); }); it("resizing the renderable changes its bounds height", function () { renderable.resize(20, 20); - expect(renderable._bounds.height).toEqual(20); + expect(renderable.getBounds().height).toEqual(20); }); }); -}); \ No newline at end of file +}); diff --git a/tests/spec/sprite-spec.js b/tests/spec/sprite-spec.js new file mode 100644 index 0000000000..91553a1a2d --- /dev/null +++ b/tests/spec/sprite-spec.js @@ -0,0 +1,22 @@ +describe("me.Sprite", function () { + var container = new me.Container(50, 50, 150, 150); + var sprite = new me.Sprite(0, 0, { + "width" : 32, + "height" : 32, + "image" : me.video.createCanvas(32, 32) + }); + + // add to a parent container + container.addChild(sprite); + + it("me.Sprite bounds return the visible part of the sprite", function () { + var bounds = sprite.getBounds(); + expect(bounds.pos.x).toEqual(50); + expect(bounds.pos.y).toEqual(50); + expect(bounds.width).toEqual(32); + expect(bounds.height).toEqual(32); + }); + + // TODO: add bounds testing when transformation are added + +}); From 997a13c4362e37c1aa2b5b27de7b6a568d235125 Mon Sep 17 00:00:00 2001 From: obiot Date: Thu, 25 Aug 2016 10:58:55 +0800 Subject: [PATCH 093/177] documentation fixes/improvements --- src/font/bitmapfont.js | 3 ++- src/physics/body.js | 2 +- src/renderable/viewport.js | 15 +++++++++------ src/vendors/es6-promise.js | 4 ++-- src/video/renderer.js | 9 +++++---- 5 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/font/bitmapfont.js b/src/font/bitmapfont.js index 7203ae638e..f0dc27f231 100644 --- a/src/font/bitmapfont.js +++ b/src/font/bitmapfont.js @@ -34,7 +34,8 @@ * @extends me.Renderable * @memberOf me * @constructor - * @param {Image} the image object for the font. Should be retrieved from the loader + * @param {Object} font the font object data. Should be retrieved from the loader + * @param {Image} image the font image itself Should be retrieved from the loader * @param {Number} [scale=1.0] * @param {String} [textAlign="left"] * @param {String} [textBaseline="top"] diff --git a/src/physics/body.js b/src/physics/body.js index 492775b44a..df13315f39 100644 --- a/src/physics/body.js +++ b/src/physics/body.js @@ -282,7 +282,7 @@ * @public * @function * @param {Number} [index=0] the shape object at the specified index - * @return {me.Polygon|me.Line|me.Ellipse} shape a shape object + * @return {me.Polygon|me.Line|me.Ellipse} shape a shape object if defined */ getShape : function (index) { return this.shapes[index || 0]; diff --git a/src/renderable/viewport.js b/src/renderable/viewport.js index 3e9cc05ec2..0ca2214b11 100644 --- a/src/renderable/viewport.js +++ b/src/renderable/viewport.js @@ -138,7 +138,7 @@ /** * change the deadzone settings. * the "deadzone" defines an area within the current viewport in which - * the followed entity can move without scrolling the viewport. + * the followed renderable can move without scrolling the viewport. * @name setDeadzone * @see me.Viewport.follow * @memberOf me.Viewport @@ -207,16 +207,17 @@ }, /** - * set the viewport to follow the specified entity + * set the viewport to follow the specified renderable.
+ * (this will put the viewport center around the given target) * @name follow * @memberOf me.Viewport * @function - * @param {me.Entity|me.Vector2d} target Entity or Position + * @param {me.Renderable|me.Vector2d} target renderable or position * Vector to follow * @param {me.Viewport.AXIS} [axis=this.AXIS.BOTH] Which axis to follow */ follow : function (target, axis) { - if (target instanceof me.Entity) { + if (target instanceof me.Renderable) { this.target = target.pos; } else if ((target instanceof me.Vector2d) || (target instanceof me.Vector3d)) { @@ -234,9 +235,10 @@ }, /** - * move the viewport position by the specified offset + * move the viewport upper-left position by the specified offset. * @name move * @memberOf me.Viewport + * @see me.Viewport.focusOn * @function * @param {Number} x * @param {Number} y @@ -249,9 +251,10 @@ }, /** - * move the viewport to the specified coordinates + * move the viewport upper-left position to the specified coordinates * @name moveTo * @memberOf me.Viewport + * @see me.Viewport.focusOn * @function * @param {Number} x * @param {Number} y diff --git a/src/vendors/es6-promise.js b/src/vendors/es6-promise.js index d415987cf3..d0c7f98c75 100644 --- a/src/vendors/es6-promise.js +++ b/src/vendors/es6-promise.js @@ -792,7 +792,7 @@ // something went wrong }); ``` - + @ignore @method then @param {Function} onFulfilled @param {Function} onRejected @@ -822,7 +822,7 @@ // something went wrong }); ``` - + @ignore @method catch @param {Function} onRejection Useful for tooling. diff --git a/src/video/renderer.js b/src/video/renderer.js index d5ef444799..4badca3079 100644 --- a/src/video/renderer.js +++ b/src/video/renderer.js @@ -235,13 +235,14 @@ }, /** - * resizes the canvas + * resizes the system canvas * @name resize * @memberOf me.Renderer * @function + * @param {Number} width new width of the canvas + * @param {Number} height new height of the canvas */ - resize : function (width, height) - { + resize : function (width, height) { this.backBufferCanvas.width = width; this.backBufferCanvas.height = height; }, @@ -249,7 +250,7 @@ /** * enable/disable image smoothing (scaling interpolation) for the specified 2d Context
* (!) this might not be supported by all browsers
- * @name setImageSmoothing + * @name setAntiAlias * @memberOf me.Renderer * @function * @param {Context2d} context From 14f84706c9de213aea22329f3ab6cc7eebeef2cf Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Tue, 30 Aug 2016 22:15:22 -0400 Subject: [PATCH 094/177] remove incorrect docs from preload --- src/loader/loader.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/loader/loader.js b/src/loader/loader.js index edd999953b..68a7745648 100644 --- a/src/loader/loader.js +++ b/src/loader/loader.js @@ -331,8 +331,6 @@ * @param {String} resources.type "audio", binary", "image", "json", "tmx", "tsx" * @param {String} resources.src path and/or file name of the resource (for audio assets only the path is required) * @param {Boolean} [resources.stream] set to true if you don't have to wait for the audio file to be fully downloaded - * @param {Function} onload function to be called when the resource is loaded - * @param {Function} onerror function to be called in case of error * @param {function} [onload=me.loader.onload] function to be called when all resources are loaded * @param {boolean} [switchToLoadState=true] automatically switch to the loading screen * @example From 110511e9dce6784be4c64fc40774a5fcf3fde9ea Mon Sep 17 00:00:00 2001 From: Aaron McLeod Date: Tue, 30 Aug 2016 22:17:08 -0400 Subject: [PATCH 095/177] add width & height props --- src/entity/entity.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/entity/entity.js b/src/entity/entity.js index 4a8e3beafe..4a8e392241 100644 --- a/src/entity/entity.js +++ b/src/entity/entity.js @@ -17,6 +17,8 @@ * @param {Number} y the y coordinates of the entity object * @param {Object} settings Entity properties, to be defined through Tiled or when calling the entity constructor * + * @param {Number} settings.width the physical width the entity takes up in game + * @param {Number} settings.height the physical height the entity takes up in game * @param {String} [settings.name] object entity name * @param {String} [settings.id] object unique IDs * @param {Image|String} [settings.image] resource name of a spritesheet to use for the entity renderable component From f897f6bb25284c2a7c1fb40516954040a107ba11 Mon Sep 17 00:00:00 2001 From: obiot Date: Wed, 31 Aug 2016 10:24:42 +0800 Subject: [PATCH 096/177] [#580] forgot that one ! --- src/renderable/renderable.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/renderable/renderable.js b/src/renderable/renderable.js index bb103179c6..950be9b693 100644 --- a/src/renderable/renderable.js +++ b/src/renderable/renderable.js @@ -235,12 +235,13 @@ * @function */ updateBoundsPos : function (newX, newY) { - this._bounds.pos.set(newX, newY); + var bounds = this.getBounds(); + bounds.pos.set(newX, newY); // XXX: This is called from the constructor, before it gets an ancestor if (this.ancestor) { - this._bounds.pos.add(this.ancestor._absPos); + bounds.pos.add(this.ancestor._absPos); } - return this._bounds; + return bounds; }, /** From 02766076f3f68cb4cd4a3113cb1e2b69fc9c9c30 Mon Sep 17 00:00:00 2001 From: obiot Date: Thu, 1 Sep 2016 15:07:00 +0800 Subject: [PATCH 097/177] rename the `transform` property to `currentTransform` after further using it in a more real case, I found it very confusing wrt to the `transform` method, and having it called "currentTransform" is more aligned with the (experimental) standard one: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/currentTransform --- src/entity/entity.js | 4 ++-- src/game.js | 4 ++-- src/level/LevelDirector.js | 6 +++--- src/level/TMXTile.js | 24 ++++++++++++------------ src/level/TMXTileset.js | 2 +- src/particles/particle.js | 4 ++-- src/renderable/container.js | 14 +++++++------- src/renderable/renderable.js | 12 ++++++------ src/renderable/sprite.js | 14 ++++++-------- 9 files changed, 41 insertions(+), 43 deletions(-) diff --git a/src/entity/entity.js b/src/entity/entity.js index d9c694d62c..a341953199 100644 --- a/src/entity/entity.js +++ b/src/entity/entity.js @@ -290,7 +290,7 @@ renderer.translate(x, y); // apply the child transform, if any - if (child.autoTransform === true && !child.transform.isIdentity()) { + if (child.autoTransform === true && !child.currentTransform.isIdentity()) { // calculate the anchor point var bounds = child.getBounds(); var cx = ~~(0.5 + (bounds.width * child.anchorPoint.x)); @@ -301,7 +301,7 @@ // translate to the anchor point renderer.translate(cx, cy); // apply the object transformation - renderer.transform(child.transform); + renderer.transform(child.currentTransform); // translate back renderer.translate(-cx, -cy); diff --git a/src/game.js b/src/game.js index e3c7d37040..ddab92b93d 100644 --- a/src/game.js +++ b/src/game.js @@ -330,7 +330,7 @@ var translateY = api.viewport.pos.y + ~~api.viewport.offset.y; // translate the world coordinates by default to screen coordinates - api.world.transform.translate(-translateX, -translateY); + api.world.currentTransform.translate(-translateX, -translateY); // prepare renderer to draw a new frame me.video.renderer.clear(); @@ -340,7 +340,7 @@ api.world.draw(renderer, api.viewport); // translate back - api.world.transform.translate(translateX, translateY); + api.world.currentTransform.translate(translateX, translateY); // draw our camera/viewport api.viewport.draw(renderer); diff --git a/src/level/LevelDirector.js b/src/level/LevelDirector.js index 00bd67ed55..e1dca6a742 100644 --- a/src/level/LevelDirector.js +++ b/src/level/LevelDirector.js @@ -196,9 +196,9 @@ * var levelContainer = new me.Container(); * me.levelDirector.loadLevel("a4_level2", {container:levelContainer}); * // add a simple transformation - * levelContainer.transform.translate(levelContainer.width / 2, levelContainer.height / 2 ); - * levelContainer.transform.rotate(0.05); - * levelContainer.transform.translate(-levelContainer.width / 2, -levelContainer.height / 2 ); + * levelContainer.currentTransform.translate(levelContainer.width / 2, levelContainer.height / 2 ); + * levelContainer.currentTransform.rotate(0.05); + * levelContainer.currentTransform.translate(-levelContainer.width / 2, -levelContainer.height / 2 ); * // add it to the game world * me.game.world.addChild(levelContainer); */ diff --git a/src/level/TMXTile.js b/src/level/TMXTile.js index 1575ff7f86..3c34f9acfa 100644 --- a/src/level/TMXTile.js +++ b/src/level/TMXTile.js @@ -42,7 +42,7 @@ * the tile transformation matrix (if defined) * @ignore */ - this.transform = null; + this.currentTransform = null; // call the parent constructor this._super(me.Rect, "init", [x * tileset.tilewidth, y * tileset.tileheight, tileset.tilewidth, tileset.tileheight]); @@ -101,35 +101,35 @@ * @ignore */ createTransform : function () { - if (this.transform === null) { - this.transform = new me.Matrix2d(); + if (this.currentTransform === null) { + this.currentTransform = new me.Matrix2d(); } else { // reset the matrix - this.transform.identity(); + this.currentTransform.identity(); } if (this.flippedAD) { // Use shearing to swap the X/Y axis - this.transform.setTransform( + this.currentTransform.setTransform( 0, 1, 0, 1, 0, 0, 0, 0, 1 ); - this.transform.translate(0, this.height - this.width); + this.currentTransform.translate(0, this.height - this.width); } if (this.flippedX) { - this.transform.translate( + this.currentTransform.translate( (this.flippedAD ? 0 : this.width), (this.flippedAD ? this.height : 0) ); - this.transform.scaleX(-1); + this.currentTransform.scaleX(-1); } if (this.flippedY) { - this.transform.translate( + this.currentTransform.translate( (this.flippedAD ? this.width : 0), (this.flippedAD ? 0 : this.height) ); - this.transform.scaleY(-1); + this.currentTransform.scaleY(-1); } }, @@ -165,10 +165,10 @@ // any H/V flipping to apply? if (this.flippedX) { - renderable.transform.scaleX(-1); + renderable.currentTransform.scaleX(-1); } if (this.flippedY) { - renderable.transform.scaleY(-1); + renderable.currentTransform.scaleY(-1); } return renderable; diff --git a/src/level/TMXTileset.js b/src/level/TMXTileset.js index ec42663022..55fe591e67 100755 --- a/src/level/TMXTileset.js +++ b/src/level/TMXTileset.js @@ -226,7 +226,7 @@ renderer.save(); // apply the tile current transform renderer.translate(dx, dy); - renderer.transform(tmxTile.transform); + renderer.transform(tmxTile.currentTransform); // reset both values as managed through transform(); dx = dy = 0; } diff --git a/src/particles/particle.js b/src/particles/particle.js index b837cb3a7d..8dcec4531e 100644 --- a/src/particles/particle.js +++ b/src/particles/particle.js @@ -126,7 +126,7 @@ this.pos.y += this.vel.y * skew; // Update particle transform - this.transform.setTransform( + this.currentTransform.setTransform( scale, 0, 0, 0, scale, 0, ~~this.pos.x, ~~this.pos.y, 1 @@ -143,7 +143,7 @@ renderer.setGlobalAlpha(renderer.globalAlpha() * this.alpha); // translate to the defined anchor point and scale it - renderer.transform(this.transform); + renderer.transform(this.currentTransform); var w = this.width, h = this.height; renderer.drawImage( diff --git a/src/renderable/container.js b/src/renderable/container.js index 32f9dd23ef..bc490a2944 100644 --- a/src/renderable/container.js +++ b/src/renderable/container.js @@ -696,7 +696,7 @@ } // reset the transformation matrix - this.transform.identity(); + this.currentTransform.identity(); }, /** @@ -766,8 +766,8 @@ renderer.translate(this.pos.x, this.pos.y); // apply the renderable transformation matrix - if (!this.transform.isIdentity()) { - renderer.transform(this.transform); + if (!this.currentTransform.isIdentity()) { + renderer.transform(this.currentTransform); } // apply the group opacity @@ -778,7 +778,7 @@ if ((obj.inViewport || isFloating) && obj.isRenderable) { - hasTransform = !obj.transform.isIdentity(); + hasTransform = !obj.currentTransform.isIdentity(); if (isFloating) { // translate to screen coordinates @@ -794,9 +794,9 @@ if (hasTransform) { renderer.save(); - obj.transform.translate(x, y); + obj.currentTransform.translate(x, y); // apply the object transformation - renderer.transform(obj.transform); + renderer.transform(obj.currentTransform); } else { renderer.translate(x, y); } @@ -811,7 +811,7 @@ } else if (obj.autoTransform === true) { if (hasTransform) { // restore the save context/global matric - obj.transform.translate(-x, -y); + obj.currentTransform.translate(-x, -y); renderer.restore(); } else { // translate back diff --git a/src/renderable/renderable.js b/src/renderable/renderable.js index 804b1ab389..ae5773fdc6 100644 --- a/src/renderable/renderable.js +++ b/src/renderable/renderable.js @@ -34,13 +34,13 @@ * the renderable default transformation matrix * @public * @type me.Matrix2d - * @name transform + * @name currentTransform * @memberOf me.Renderable */ - if (typeof this.transform !== "undefined") { - this.transform.identity(); + if (typeof this.currentTransform !== "undefined") { + this.currentTransform.identity(); } else { - this.transform = new me.Matrix2d(); + this.currentTransform = new me.Matrix2d(); } /** @@ -132,7 +132,7 @@ * // enable "automatic" transformation when the object is activated * onActivateEvent: function () { * // reset the transformation matrix - * this.renderable.transform.identity(); + * this.renderable.currentTransform.identity(); * // ensure the anchor point is the renderable center * this.renderable.anchorPoint.set(0.5, 0.5); * // enable auto transform @@ -142,7 +142,7 @@ * // add a rotation effect when updating the entity * update : function (dt) { * .... - * this.renderable.transform.rotate(0.025); + * this.renderable.currentTransform.rotate(0.025); * .... * return this._super(me.Entity, 'update', [dt]); * }, diff --git a/src/renderable/sprite.js b/src/renderable/sprite.js index 281c1c6d83..d06e1ea19d 100644 --- a/src/renderable/sprite.js +++ b/src/renderable/sprite.js @@ -110,7 +110,7 @@ // set the default rotation angle is defined in the settings // * WARNING: rotating sprites decreases performance with Canvas Renderer if (typeof (settings.rotation) !== "undefined") { - this.transform.rotate(settings.rotation); + this.currentTransform.rotate(settings.rotation); } // update anchorPoint @@ -169,9 +169,7 @@ if (flip !== this.lastflipX) { this.lastflipX = flip; // invert the scale.x value - this.transform.scaleX(-1); - - //console.log(this.transform._scale.x); + this.currentTransform.scaleX(-1); } }, @@ -187,7 +185,7 @@ if (flip !== this.lastflipY) { this.lastflipY = flip; // invert the scale.x value - this.transform.scaleY(-1); + this.currentTransform.scaleY(-1); } }, @@ -203,7 +201,7 @@ console.warn("Deprecated: me.Sprite.scale"); // set the scaleFlag - this.transform.scale(x, y); + this.currentTransform.scale(x, y); // resize the bounding box this.getBounds().resize(this.width * x, this.height * y); @@ -279,8 +277,8 @@ renderer.setGlobalAlpha(renderer.globalAlpha() * this.getOpacity()); // apply the renderable transformation matrix - if (!this.transform.isIdentity()) { - renderer.transform(this.transform); + if (!this.currentTransform.isIdentity()) { + renderer.transform(this.currentTransform); } // translate to the defined anchor point From b86fabf736fa39d1fbace7d0dcabb2ef35052873 Mon Sep 17 00:00:00 2001 From: obiot Date: Thu, 1 Sep 2016 15:08:07 +0800 Subject: [PATCH 098/177] fix a regression in updateBoundsPos the issue was only visible when running the particle example --- src/renderable/renderable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderable/renderable.js b/src/renderable/renderable.js index ae5773fdc6..687117d86d 100644 --- a/src/renderable/renderable.js +++ b/src/renderable/renderable.js @@ -278,7 +278,7 @@ */ updateBoundsPos : function (newX, newY) { var bounds = this.getBounds(); - bounds.pos.set(newX, newY); + bounds.pos.set(newX, newY, bounds.pos.z); // XXX: This is called from the constructor, before it gets an ancestor if (this.ancestor) { bounds.pos.add(this.ancestor._absPos); From c188a9e1ae2f47a5465fedc270beab63407d7227 Mon Sep 17 00:00:00 2001 From: Jay Oster Date: Thu, 1 Sep 2016 21:33:54 -0700 Subject: [PATCH 099/177] Workaround Jasmine bug https://github.com/jasmine/jasmine/issues/1188 --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 0c887e7941..de53a9486c 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "grunt-jsdoc": "^1.1.0", "grunt-replace": "^0.11.0", "grunt-shell": "^1.1.2", + "jasmine-core": "2.4.1", "q": "^1.4.1", "shelljs": "^0.5.3" }, From 05712b1aed1d1dbda015e9a5ec95074d4fbe94c1 Mon Sep 17 00:00:00 2001 From: obiot Date: Mon, 5 Sep 2016 15:06:20 +0800 Subject: [PATCH 100/177] [#771] polygon shape transformations with matrix2d also add a `transform` method for renderable, as they do inherit from me.Rect/me.Polygon, and this was causing cohesion issue with the parent API vs the object implementation, --- src/renderable/renderable.js | 14 ++++++++++++++ src/shapes/ellipse.js | 13 +++++++++++++ src/shapes/poly.js | 19 +++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/src/renderable/renderable.js b/src/renderable/renderable.js index 687117d86d..3d55e3f41c 100644 --- a/src/renderable/renderable.js +++ b/src/renderable/renderable.js @@ -255,6 +255,20 @@ } }, + /** + * multiply the renderable currentTransform with the given matrix + * @name transform + * @memberOf me.Renderable + * @see me.Renderable#currentTransform + * @function + * @param {me.Matrix2d} matrix the transformation matrix + * @return {me.Polygon} Reference to this object for method chaining + */ + transform : function (m) { + this.currentTransform.multiply(m); + return this; + }, + /** * update function * called by the game manager on each game loop diff --git a/src/shapes/ellipse.js b/src/shapes/ellipse.js index bd5d719705..16c423fa16 100644 --- a/src/shapes/ellipse.js +++ b/src/shapes/ellipse.js @@ -147,6 +147,19 @@ return this.scale(v.x, v.y); }, + /** + * apply the given transformation matrix to this ellipse + * @name transform + * @memberOf me.Ellipse + * @function + * @param {me.Matrix2d} matrix the transformation matrix + * @return {me.Polygon} Reference to this object for method chaining + */ + transform : function (m) { + // TODO + return this; + }, + /** * translate the circle/ellipse by the specified offset * @name translate diff --git a/src/shapes/poly.js b/src/shapes/poly.js index 1a9dfdeaf1..ed11368134 100644 --- a/src/shapes/poly.js +++ b/src/shapes/poly.js @@ -75,6 +75,25 @@ return this; }, + /** + * apply the given transformation matrix to this Polygon + * @name transform + * @memberOf me.Polygon + * @function + * @param {me.Matrix2d} matrix the transformation matrix + * @return {me.Polygon} Reference to this object for method chaining + */ + transform : function (m) { + var points = this.points; + var len = points.length; + for (var i = 0; i < len; i++) { + m.multiplyVector(points[i]); + } + this.recalc(); + this.updateBounds(); + return this; + }, + /** * Rotate this Polygon (counter-clockwise) by the specified angle (in radians). * @name rotate From c6c740ebe0bbcc642e395a3fd47867bd6c12cd8c Mon Sep 17 00:00:00 2001 From: obiot Date: Mon, 5 Sep 2016 15:07:19 +0800 Subject: [PATCH 101/177] [#771] fix code linting --- src/shapes/ellipse.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shapes/ellipse.js b/src/shapes/ellipse.js index 16c423fa16..ea8770ad6c 100644 --- a/src/shapes/ellipse.js +++ b/src/shapes/ellipse.js @@ -155,7 +155,7 @@ * @param {me.Matrix2d} matrix the transformation matrix * @return {me.Polygon} Reference to this object for method chaining */ - transform : function (m) { + transform : function (/* m */) { // TODO return this; }, From 440b16a867a4801e4af2cb24d86e0ee4985d7d41 Mon Sep 17 00:00:00 2001 From: obiot Date: Tue, 6 Sep 2016 17:16:18 +0800 Subject: [PATCH 102/177] make it a bit less "hackish" by using actual Ellipse object for the collision test i don't even understand how it was working before, as Renderable objects have no `radius` or any other ellipse specific properties defined --- tests/spec/ellipse-spec.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/spec/ellipse-spec.js b/tests/spec/ellipse-spec.js index fc798b7a9f..8015095be3 100644 --- a/tests/spec/ellipse-spec.js +++ b/tests/spec/ellipse-spec.js @@ -2,11 +2,11 @@ describe("Shape : me.Ellipse", function () { // define a ellipse object var circle = new me.Ellipse(50, 50, 100, 100); - var renderableA = new me.Renderable(0, 0, 100, 100); - var renderableB = new me.Renderable(0, 50, 100, 100); + var circleA = new me.Ellipse(0, 0, 100, 100); + var circleB = new me.Ellipse(0, 50, 100, 100); // mock ancestor - circle.ancestor = renderableA.ancestor = renderableB.ancestor = { + circle.ancestor = circleA.ancestor = circleB.ancestor = { "_absPos" : { "x" : 0, "y" : 0 @@ -44,8 +44,8 @@ describe("Shape : me.Ellipse", function () { it("collision response is correct", function () { expect(me.collision.testEllipseEllipse( - renderableA, circle, - renderableB, circle, + circleA, circle, + circleB, circle, me.collision.response.clear() )).toEqual(true); expect(me.collision.response.overlap).toEqual(50); From 7920a9be49dcc3eb21de5f33cf5b715c6ad06ec9 Mon Sep 17 00:00:00 2001 From: obiot Date: Tue, 6 Sep 2016 21:07:24 +0800 Subject: [PATCH 103/177] fix execution of test units using the pooling mechanism it does not feel right, as it more looks like a workaround, but adding a call to me.boot() in `helper-specs` did not do the trick.... --- testSpecs.json | 1 + tests/index.html | 3 ++- tests/spec/color-spec.js | 7 ++++++- tests/spec/init.js | 3 +++ 4 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 tests/spec/init.js diff --git a/testSpecs.json b/testSpecs.json index 26f70bdb9e..b30cd1818e 100644 --- a/testSpecs.json +++ b/testSpecs.json @@ -1,4 +1,5 @@ [ + "tests/spec/init.js", "tests/spec/color-spec.js", "tests/spec/container-spec.js", "tests/spec/mat2d-spec.js", diff --git a/tests/index.html b/tests/index.html index 593e46b6b0..3bd02c595e 100644 --- a/tests/index.html +++ b/tests/index.html @@ -2,7 +2,7 @@ melonJS unit-tests - + @@ -21,6 +21,7 @@ + diff --git a/tests/spec/color-spec.js b/tests/spec/color-spec.js index d66c3d9fcc..fea4d712ad 100644 --- a/tests/spec/color-spec.js +++ b/tests/spec/color-spec.js @@ -107,7 +107,12 @@ describe("me.Color", function () { it("lighten blue_color hex by 0.5 value is #7F7FFF", function () { expect(blue_color.lighten(0.5).toHex()).toEqual("#7F7FFF"); }); + }); - + describe("color clone function", function () { + var clone = blue_color.clone(); + it("clone color hex value is #0000FF", function () { + expect(clone.toHex()).toEqual("#0000FF"); + }); }); }); diff --git a/tests/spec/init.js b/tests/spec/init.js new file mode 100644 index 0000000000..deb4d8709e --- /dev/null +++ b/tests/spec/init.js @@ -0,0 +1,3 @@ +describe("melonJS initialization", function () { + me.boot(); +}); From 8b3d5c2d76ee461dc858ea5aa3d0955342142bfb Mon Sep 17 00:00:00 2001 From: obiot Date: Tue, 6 Sep 2016 21:37:45 +0800 Subject: [PATCH 104/177] [#807] allow recycling of `me.Matrix2d` --- src/entity/entity.js | 3 +++ src/math/matrix2.js | 14 ++++++++++---- src/renderable/renderable.js | 4 +++- src/system/pooling.js | 1 + 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/entity/entity.js b/src/entity/entity.js index a341953199..c82df6ecba 100644 --- a/src/entity/entity.js +++ b/src/entity/entity.js @@ -329,6 +329,9 @@ } this.body.destroy.apply(this.body, arguments); this.body = null; + + // call the parent destroy method + this._super(me.Renderable, "destroy", arguments); }, /** diff --git a/src/math/matrix2.js b/src/math/matrix2.js index 7baabc5839..aca7bfd727 100644 --- a/src/math/matrix2.js +++ b/src/math/matrix2.js @@ -21,7 +21,9 @@ /** @ignore */ init : function () { - this.val = new Float32Array(9); + if (typeof(this.val) === "undefined") { + this.val = new Float32Array(9); + } if (arguments.length && arguments[0] instanceof me.Matrix2d) { this.copy(arguments[0]); } @@ -29,10 +31,15 @@ this.setTransform.apply(this, arguments); } else { - this.identity(); + this.onResetEvent(); } }, + /** @ignore */ + onResetEvent : function() { + this.identity(); + }, + /** * reset the transformation matrix to the identity matrix (no transformation).
* the identity matrix and parameters position :
@@ -48,7 +55,6 @@ 0, 1, 0, 0, 0, 1 ); - return this; }, @@ -306,7 +312,7 @@ * @return {me.Matrix2d} */ clone : function () { - return new me.Matrix2d(this); + return me.pool.pull("me.Matrix2d").copy(this); }, /** diff --git a/src/renderable/renderable.js b/src/renderable/renderable.js index 3d55e3f41c..3dd5ae9310 100644 --- a/src/renderable/renderable.js +++ b/src/renderable/renderable.js @@ -40,7 +40,7 @@ if (typeof this.currentTransform !== "undefined") { this.currentTransform.identity(); } else { - this.currentTransform = new me.Matrix2d(); + this.currentTransform = me.pool.pull("me.Matrix2d"); } /** @@ -331,6 +331,8 @@ * @ignore */ destroy : function () { + me.pool.push(this.currentTransform); + this.currentTransform = undefined; this.onDestroyEvent.apply(this, arguments); }, diff --git a/src/system/pooling.js b/src/system/pooling.js index 9f466e3186..be6e3155e3 100644 --- a/src/system/pooling.js +++ b/src/system/pooling.js @@ -46,6 +46,7 @@ api.register("me.Sprite", me.Sprite); api.register("me.Vector2d", me.Vector2d, true); api.register("me.Glyph", me.Glyph, true); + api.register("me.Matrix2d", me.Matrix2d, true); }; /** From 8024852d06e1129a7e194c11940da749a114ca57 Mon Sep 17 00:00:00 2001 From: obiot Date: Tue, 6 Sep 2016 22:09:18 +0800 Subject: [PATCH 105/177] this is not required anymore working just fine without it now --- src/loader/loadingscreen.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/loader/loadingscreen.js b/src/loader/loadingscreen.js index 51bc41fbc4..50fed9bbd5 100644 --- a/src/loader/loadingscreen.js +++ b/src/loader/loadingscreen.js @@ -17,8 +17,6 @@ // current progress this.progress = 0; - - this.autoTransform = false; }, // make sure the screen is refreshed every frame @@ -88,8 +86,6 @@ context.lineJoin = "miter"; context.miterLimit = 4.0; context.stroke(); - - this.autoTransform = false; }, draw : function (renderer) { @@ -106,8 +102,6 @@ this.logo2 = new me.Font("century gothic", 32, "#55aa00", "middle"); this.logo2.bold(); this.logo1.textBaseline = this.logo2.textBaseline = "alphabetic"; - - this.autoTransform = false; }, draw : function (renderer) { From f6bd52d90b47913ab00e4a555d96fcb79647b55f Mon Sep 17 00:00:00 2001 From: obiot Date: Thu, 8 Sep 2016 11:40:52 +0800 Subject: [PATCH 106/177] this has nothing to do here as well --- tests/spec/color-spec.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/spec/color-spec.js b/tests/spec/color-spec.js index fea4d712ad..dd7cca9830 100644 --- a/tests/spec/color-spec.js +++ b/tests/spec/color-spec.js @@ -4,8 +4,6 @@ describe("me.Color", function () { var green_color = new me.Color().parseCSS("green"); var blue_color = new me.Color().parseHex("#0000FF"); - me.video.renderer = me.CanvasRenderer; - describe("parseHex Function", function () { // #RGB it("#00F value is rgb(0, 0, 255)", function () { From 8a55445ec342bda3fbdf18495d7ae0d640d03bb4 Mon Sep 17 00:00:00 2001 From: obiot Date: Thu, 8 Sep 2016 15:43:47 +0800 Subject: [PATCH 107/177] [#777] merge me.Animation into me.Sprite me.Animation is now only an alias (for backward compatibility purpose) --- sourceFiles.json | 1 - src/renderable/animationsheet.js | 347 -------------------------- src/renderable/sprite.js | 414 ++++++++++++++++++++++++++----- tests/spec/init.js | 1 + tests/spec/sprite-spec.js | 10 +- 5 files changed, 361 insertions(+), 412 deletions(-) delete mode 100644 src/renderable/animationsheet.js diff --git a/sourceFiles.json b/sourceFiles.json index d4581c54ca..5ffc28e1bd 100644 --- a/sourceFiles.json +++ b/sourceFiles.json @@ -31,7 +31,6 @@ "src/physics/collision.js", "src/renderable/renderable.js", "src/renderable/sprite.js", - "src/renderable/animationsheet.js", "src/renderable/viewport.js", "src/renderable/GUI.js", "src/renderable/container.js", diff --git a/src/renderable/animationsheet.js b/src/renderable/animationsheet.js deleted file mode 100644 index 5e14bbb364..0000000000 --- a/src/renderable/animationsheet.js +++ /dev/null @@ -1,347 +0,0 @@ -/* - * MelonJS Game Engine - * Copyright (C) 2011 - 2016, Olivier Biot, Jason Oster, Aaron McLeod - * http://www.melonjs.org - * - */ -(function () { - - /** - * an object to manage animation - * @class - * @extends me.Sprite - * @memberOf me - * @constructor - * @param {Number} x the x coordinates of the sprite object - * @param {Number} y the y coordinates of the sprite object - * @param {Object} settings Contains additional parameters for the animation sheet - * @param {me.video.renderer.Texture|Image|String} settings.image reference to a spritesheet image or to a texture atlas - * @param {Number} [settings.framewidth] Width of a single frame within the spritesheet - * @param {Number} [settings.frameheight] Height of a single frame within the spritesheet - * @param {me.Vector2d} [settings.anchorPoint={x:0.5, y:0.5}] Anchor point to draw the frame at (defaults to the center of the frame). - * @example - * // standalone image, with anchor in the center - * var animationSheet = new me.AnimationSheet(0, 0, { - * image : "animationsheet", - * framewidth : 64, - * frameheight : 64, - * anchorPoint : new me.Vector2d(0.5, 0.5) - * }); - */ - me.AnimationSheet = me.Sprite.extend( - /** @scope me.AnimationSheet.prototype */ - { - /** @ignore */ - init : function (x, y, settings) { - - /** - * pause and resume animation
- * default value : false; - * @public - * @type Boolean - * @name me.AnimationSheet#animationpause - */ - this.animationpause = false; - - /** - * animation cycling speed (delay between frame in ms)
- * default value : 100ms; - * @public - * @type Number - * @name me.AnimationSheet#animationspeed - */ - this.animationspeed = 100; - - // hold all defined animation - this.anim = {}; - - // a flag to reset animation - this.resetAnim = null; - - // default animation sequence - this.current = null; - - // animation frame delta - this.dt = 0; - - // call the constructor - this._super(me.Sprite, "init", [ x, y, settings ]); - - // store/reset the current atlas information if specified - if (typeof(settings.atlas) !== "undefined") { - this.textureAtlas = settings.atlas; - this.atlasIndices = settings.atlasIndices; - } else { - // set the texture Atlas object from the given texture - if (settings.image instanceof me.CanvasRenderer.prototype.Texture) { - this.textureAtlas = settings.image; - } else { - // "regular" spritesheet - this.textureAtlas = me.video.renderer.cache.get( - me.utils.getImage(settings.image), - settings - ).getAtlas(); - } - this.atlasIndices = null; - } - - // create a default animation sequence with all sprites - this.addAnimation("default", null); - - // set as default - this.setCurrentAnimation("default"); - }, - - /** - * add an animation
- * For fixed-sized cell sprite sheet, the index list must follow the - * logic as per the following example :
- * - * @name addAnimation - * @memberOf me.AnimationSheet - * @function - * @param {String} name animation id - * @param {Number[]|String[]|Object[]} index list of sprite index or name - * defining the animation. Can also use objects to specify delay for each frame, see below - * @param {Number} [animationspeed] cycling speed for animation in ms - * (delay between each frame). - * @see me.AnimationSheet#animationspeed - * @example - * // walking animation - * this.addAnimation("walk", [ 0, 1, 2, 3, 4, 5 ]); - * // eating animation - * this.addAnimation("eat", [ 6, 6 ]); - * // rolling animation - * this.addAnimation("roll", [ 7, 8, 9, 10 ]); - * // slower animation - * this.addAnimation("roll", [ 7, 8, 9, 10 ], 200); - * // or get more specific with delay for each frame. Good solution instead of repeating: - * this.addAnimation("turn", [{ name: 0, delay: 200 }, { name: 1, delay: 100 }]) - * // can do this with atlas values as well: - * this.addAnimation("turn", [{ name: "turnone", delay: 200 }, { name: "turntwo", delay: 100 }]) - * // define an dying animation that stop on the last frame - * this.addAnimation("die", [{ name: 3, delay: 200 }, { name: 4, delay: 100 }, { name: 5, delay: Infinity }]) - */ - addAnimation : function (name, index, animationspeed) { - this.anim[name] = { - name : name, - frames : [], - idx : 0, - length : 0 - }; - - if (index == null) { - index = []; - // create a default animation with all frame - Object.keys(this.textureAtlas).forEach(function (v, i) { - index[i] = i; - }); - } - - // # of frames - var counter = 0; - // set each frame configuration (offset, size, etc..) - for (var i = 0, len = index.length; i < len; i++) { - var frame = index[i]; - var frameObject; - if (typeof(frame) === "number" || typeof(frame) === "string") { - frameObject = { - name: frame, - delay: animationspeed || this.animationspeed - }; - } - else { - frameObject = frame; - } - var frameObjectName = frameObject.name; - if (typeof(frameObjectName) === "number") { - if (typeof (this.textureAtlas[frameObjectName]) !== "undefined") { - // TODO: adding the cache source coordinates add undefined entries in webGL mode - this.anim[name].frames[i] = Object.assign( - {}, - this.textureAtlas[frameObjectName], - frameObject - ); - counter++; - } - } else { // string - if (this.atlasIndices === null) { - throw new me.Renderable.Error( - "string parameters for addAnimation are not allowed for standard spritesheet based Texture" - ); - } else { - this.anim[name].frames[i] = Object.assign( - {}, - this.textureAtlas[this.atlasIndices[frameObjectName]], - frameObject - ); - counter++; - } - } - } - this.anim[name].length = counter; - }, - - /** - * set the current animation - * this will always change the animation & set the frame to zero - * @name setCurrentAnimation - * @memberOf me.AnimationSheet - * @function - * @param {String} name animation id - * @param {String|Function} [onComplete] animation id to switch to when - * complete, or callback - * @example - * // set "walk" animation - * this.setCurrentAnimation("walk"); - * - * // set "walk" animation if it is not the current animation - * if (this.isCurrentAnimation("walk")) { - * this.setCurrentAnimation("walk"); - * } - * - * // set "eat" animation, and switch to "walk" when complete - * this.setCurrentAnimation("eat", "walk"); - * - * // set "die" animation, and remove the object when finished - * this.setCurrentAnimation("die", (function () { - * me.game.world.removeChild(this); - * return false; // do not reset to first frame - * }).bind(this)); - * - * // set "attack" animation, and pause for a short duration - * this.setCurrentAnimation("die", (function () { - * this.animationpause = true; - * - * // back to "standing" animation after 1 second - * setTimeout(function () { - * this.setCurrentAnimation("standing"); - * }, 1000); - * - * return false; // do not reset to first frame - * }).bind(this)); - **/ - setCurrentAnimation : function (name, resetAnim, _preserve_dt) { - if (this.anim[name]) { - this.current = this.anim[name]; - this.resetAnim = resetAnim || null; - this.setAnimationFrame(this.current.idx); - if (!_preserve_dt) { - this.dt = 0; - } - } else { - throw new me.Renderable.Error("animation id '" + name + "' not defined"); - } - }, - - /** - * return true if the specified animation is the current one. - * @name isCurrentAnimation - * @memberOf me.AnimationSheet - * @function - * @param {String} name animation id - * @return {Boolean} - * @example - * if (!this.isCurrentAnimation("walk")) { - * // do something funny... - * } - */ - isCurrentAnimation : function (name) { - return this.current.name === name; - }, - - /** - * force the current animation frame index. - * @name setAnimationFrame - * @memberOf me.AnimationSheet - * @function - * @param {Number} [index=0] animation frame index - * @example - * // reset the current animation to the first frame - * this.setAnimationFrame(); - */ - setAnimationFrame : function (idx) { - this.current.idx = (idx || 0) % this.current.length; - var frame = this.getAnimationFrameObjectByIndex(this.current.idx); - this.offset = frame.offset; - this.width = frame.width; - this.height = frame.height; - this._sourceAngle = frame.angle; - if (frame.anchorPoint) { - this.anchorPoint = frame.anchorPoint; - } - }, - - /** - * return the current animation frame index. - * @name getCurrentAnimationFrame - * @memberOf me.AnimationSheet - * @function - * @return {Number} current animation frame index - */ - getCurrentAnimationFrame : function () { - return this.current.idx; - }, - - /** - * Returns the frame object by the index. - * @name getAnimationFrameObjectByIndex - * @memberOf me.AnimationSheet - * @function - * @private - * @return {Number} if using number indices. Returns {Object} containing frame data if using texture atlas - */ - getAnimationFrameObjectByIndex : function (id) { - return this.current.frames[id]; - }, - - /** - * update the animation
- * this is automatically called by the game manager {@link me.game} - * @name update - * @memberOf me.AnimationSheet - * @function - * @protected - * @param {Number} dt time since the last update in milliseconds. - */ - update : function (dt) { - // Update animation if necessary - if (this.animationpause || this.current.length <= 1) { - return this._super(me.Sprite, "update", [ dt ]); - } - - var duration = 0, - result = false; - - this.dt += dt; - duration = this.getAnimationFrameObjectByIndex(this.current.idx).delay; - while (this.dt >= duration) { - result = true; - this.dt -= duration; - this.setAnimationFrame(this.current.idx + 1); - - // Switch animation if we reach the end of the strip and a callback is defined - if (this.current.idx === 0 && this.resetAnim) { - // If string, change to the corresponding animation - if (typeof this.resetAnim === "string") { - this.setCurrentAnimation(this.resetAnim, null, true); - } - // Otherwise is must be callable - else if (this.resetAnim() === false) { - // Reset to last frame - this.setAnimationFrame(this.current.length - 1); - - // Bail early without skipping any more frames. - this.dt %= duration; - break; - } - } - - // Get next frame duration - duration = this.getAnimationFrameObjectByIndex(this.current.idx).delay; - } - - return this._super(me.Sprite, "update", [ dt ]) || result; - } - }); -})(); diff --git a/src/renderable/sprite.js b/src/renderable/sprite.js index d06e1ea19d..817b694326 100644 --- a/src/renderable/sprite.js +++ b/src/renderable/sprite.js @@ -5,46 +5,52 @@ * */ (function () { + /** - * A Simple object to display a sprite on screen. + * A object to display a fixed or animated sprite on screen. * @class - * @extends me.Renderable + * @extends me.Sprite * @memberOf me * @constructor * @param {Number} x the x coordinates of the sprite object * @param {Number} y the y coordinates of the sprite object - * @param {Object} settings Contains additional parameters for the sprite - * @param {me.video.renderer.Texture|Image|String} settings.image reference to a sprite image or to a texture atlas. - * @param {String} [settings.region] the region name containing the sprite within a specified texture atlas - * @param {Number} [settings.framewidth=settings.image.width] Image source width. - * @param {Number} [settings.frameheight=settings.image.height] Image source height. - * @param {Number} [settings.rotation] Initial rotation angle in radians. - * @param {Boolean} [settings.flipX] Initial flip for X-axis. - * @param {Boolean} [settings.flipY] Initial flip for Y-axis. + * @param {Object} settings Contains additional parameters for the animation sheet + * @param {me.video.renderer.Texture|Image|String} settings.image reference to a texture, spritesheet image or to a texture atlas + * @param {Number} [settings.framewidth] Width of a single frame within the spritesheet + * @param {Number} [settings.frameheight] Height of a single frame within the spritesheet * @param {me.Vector2d} [settings.anchorPoint={x:0.5, y:0.5}] Anchor point to draw the frame at (defaults to the center of the frame). * @example - * // create a static Sprite Object - * mySprite = new me.Sprite (100, 100, { - * image : me.loader.getImage("mySpriteImage") + * // standalone image, with anchor in the center + * var sprite = new me.Sprite(0, 0, { + * image : "PlayerTexture", + * framewidth : 64, + * frameheight : 64, + * anchorPoint : new me.Vector2d(0.5, 0.5) * }); */ me.Sprite = me.Renderable.extend( - /** @scope me.Sprite.prototype */ + /** @scope .prototype */ { - /** - * @ignore - */ + /** @ignore */ init : function (x, y, settings) { - // just to keep track of when we flip - this.lastflipX = false; - this.lastflipY = false; - if (typeof (settings.flipX) !== "undefined") { - this.flipX(!!settings.flipX); - } - if (typeof (settings.flipY) !== "undefined") { - this.flipY(!!settings.flipY); - } + /** + * pause and resume animation
+ * default value : false; + * @public + * @type Boolean + * @name me.Sprite#animationpause + */ + this.animationpause = false; + + /** + * animation cycling speed (delay between frame in ms)
+ * default value : 100ms; + * @public + * @type Number + * @name me.Sprite#animationspeed + */ + this.animationspeed = 100; // current frame texture offset /** @@ -63,26 +69,50 @@ */ this._sourceAngle = 0; - // to manage the flickering effect - this.flickering = false; - this.flickerDuration = 0; - this.flickercb = null; - this.flickerState = false; + // hold all defined animation + this.anim = {}; + + // a flag to reset animation + this.resetAnim = null; + + // default animation sequence + this.current = null; + + // animation frame delta + this.dt = 0; + + // just to keep track of when we flip + this.lastflipX = false; + this.lastflipY = false; + + if (typeof (settings.flipX) !== "undefined") { + this.flipX(!!settings.flipX); + } + if (typeof (settings.flipY) !== "undefined") { + this.flipY(!!settings.flipY); + } + + // flicker settings + this._flicker = { + isFlickering : false, + duration : 0, + callback : null, + state : false + }; // Used by the game engine to adjust visibility as the // sprite moves in and out of the viewport this.isSprite = true; // set the proper image/texture to use - var image = settings.image; - - if (image instanceof me.CanvasRenderer.prototype.Texture) { + if (settings.image instanceof me.CanvasRenderer.prototype.Texture) { // use the texture from the texture Atlas - this.image = image.getTexture(); + this.image = settings.image.getTexture(); + this.textureAtlas = settings.image; // check for defined region if (typeof (settings.region) !== "undefined") { // use a texture atlas - var region = image.getRegion(settings.region); + var region = settings.image.getRegion(settings.region); if (region) { // set the sprite offset within the texture this.offset.setV(region.offset); @@ -96,8 +126,19 @@ } } } else { - // standard image - this.image = me.utils.getImage(image); + // standard image or spritesheet + this.image = me.utils.getImage(settings.image); + settings.framewidth = settings.framewidth || this.image.width; + settings.frameheight = settings.frameheight || this.image.height; + this.textureAtlas = me.video.renderer.cache.get(this.image, settings).getAtlas(); + } + + // store/reset the current atlas information if specified + if (typeof(settings.atlas) !== "undefined") { + this.textureAtlas = settings.atlas; + this.atlasIndices = settings.atlasIndices; + } else { + this.atlasIndices = null; } // call the super constructor @@ -118,6 +159,11 @@ this.anchorPoint.set(settings.anchorPoint.x, settings.anchorPoint.y); } + // for sprite, addAnimation will return !=0 + if (this.addAnimation("default", null) !== 0) { + // set as default + this.setCurrentAnimation("default"); + } }, /** @@ -128,7 +174,7 @@ * @return {Boolean} */ isFlickering : function () { - return this.flickering; + return this._flicker.isFlickering; }, /** @@ -146,14 +192,14 @@ * }); */ flicker : function (duration, callback) { - this.flickerDuration = duration; - if (this.flickerDuration <= 0) { - this.flickering = false; - this.flickercb = null; + this._flicker.duration = duration; + if (this._flicker.duration <= 0) { + this._flicker.isFlickering = false; + this._flicker.callback = null; } - else if (!this.flickering) { - this.flickercb = callback; - this.flickering = true; + else if (!this._flicker.isFlickering) { + this._flicker.callback = callback; + this._flicker.isFlickering = true; } }, @@ -220,28 +266,271 @@ }, /** - * sprite update
- * not to be called by the end user
- * called by the game manager on each game loop + * add an animation
+ * For fixed-sized cell sprite sheet, the index list must follow the + * logic as per the following example :
+ * + * @name addAnimation + * @memberOf me.Sprite + * @function + * @param {String} name animation id + * @param {Number[]|String[]|Object[]} index list of sprite index or name + * defining the animation. Can also use objects to specify delay for each frame, see below + * @param {Number} [animationspeed] cycling speed for animation in ms + * @return {Number} frame amount of frame added to the animation + * (delay between each frame). + * @see me.Sprite#animationspeed + * @example + * // walking animation + * this.addAnimation("walk", [ 0, 1, 2, 3, 4, 5 ]); + * // eating animation + * this.addAnimation("eat", [ 6, 6 ]); + * // rolling animation + * this.addAnimation("roll", [ 7, 8, 9, 10 ]); + * // slower animation + * this.addAnimation("roll", [ 7, 8, 9, 10 ], 200); + * // or get more specific with delay for each frame. Good solution instead of repeating: + * this.addAnimation("turn", [{ name: 0, delay: 200 }, { name: 1, delay: 100 }]) + * // can do this with atlas values as well: + * this.addAnimation("turn", [{ name: "turnone", delay: 200 }, { name: "turntwo", delay: 100 }]) + * // define an dying animation that stop on the last frame + * this.addAnimation("die", [{ name: 3, delay: 200 }, { name: 4, delay: 100 }, { name: 5, delay: Infinity }]) + */ + addAnimation : function (name, index, animationspeed) { + this.anim[name] = { + name : name, + frames : [], + idx : 0, + length : 0 + }; + + // # of frames + var counter = 0; + + if (typeof (this.textureAtlas) !== "object") { + return 0; + } + + + if (index == null) { + index = []; + // create a default animation with all frame + Object.keys(this.textureAtlas).forEach(function (v, i) { + index[i] = i; + }); + } + + // set each frame configuration (offset, size, etc..) + for (var i = 0, len = index.length; i < len; i++) { + var frame = index[i]; + var frameObject; + if (typeof(frame) === "number" || typeof(frame) === "string") { + frameObject = { + name: frame, + delay: animationspeed || this.animationspeed + }; + } + else { + frameObject = frame; + } + var frameObjectName = frameObject.name; + if (typeof(frameObjectName) === "number") { + if (typeof (this.textureAtlas[frameObjectName]) !== "undefined") { + // TODO: adding the cache source coordinates add undefined entries in webGL mode + this.anim[name].frames[i] = Object.assign( + {}, + this.textureAtlas[frameObjectName], + frameObject + ); + counter++; + } + } else { // string + if (this.atlasIndices === null) { + throw new me.Renderable.Error( + "string parameters for addAnimation are not allowed for standard spritesheet based Texture" + ); + } else { + this.anim[name].frames[i] = Object.assign( + {}, + this.textureAtlas[this.atlasIndices[frameObjectName]], + frameObject + ); + counter++; + } + } + } + this.anim[name].length = counter; + + return counter; + }, + + /** + * set the current animation + * this will always change the animation & set the frame to zero + * @name setCurrentAnimation + * @memberOf me.Sprite + * @function + * @param {String} name animation id + * @param {String|Function} [onComplete] animation id to switch to when + * complete, or callback + * @example + * // set "walk" animation + * this.setCurrentAnimation("walk"); + * + * // set "walk" animation if it is not the current animation + * if (this.isCurrentAnimation("walk")) { + * this.setCurrentAnimation("walk"); + * } + * + * // set "eat" animation, and switch to "walk" when complete + * this.setCurrentAnimation("eat", "walk"); + * + * // set "die" animation, and remove the object when finished + * this.setCurrentAnimation("die", (function () { + * me.game.world.removeChild(this); + * return false; // do not reset to first frame + * }).bind(this)); + * + * // set "attack" animation, and pause for a short duration + * this.setCurrentAnimation("die", (function () { + * this.animationpause = true; + * + * // back to "standing" animation after 1 second + * setTimeout(function () { + * this.setCurrentAnimation("standing"); + * }, 1000); + * + * return false; // do not reset to first frame + * }).bind(this)); + **/ + setCurrentAnimation : function (name, resetAnim, _preserve_dt) { + if (this.anim[name]) { + this.current = this.anim[name]; + this.resetAnim = resetAnim || null; + this.setAnimationFrame(this.current.idx); + if (!_preserve_dt) { + this.dt = 0; + } + } else { + throw new me.Renderable.Error("animation id '" + name + "' not defined"); + } + }, + + /** + * return true if the specified animation is the current one. + * @name isCurrentAnimation + * @memberOf me.Sprite + * @function + * @param {String} name animation id + * @return {Boolean} + * @example + * if (!this.isCurrentAnimation("walk")) { + * // do something funny... + * } + */ + isCurrentAnimation : function (name) { + return this.current.name === name; + }, + + /** + * force the current animation frame index. + * @name setAnimationFrame + * @memberOf me.Sprite + * @function + * @param {Number} [index=0] animation frame index + * @example + * // reset the current animation to the first frame + * this.setAnimationFrame(); + */ + setAnimationFrame : function (idx) { + this.current.idx = (idx || 0) % this.current.length; + var frame = this.getAnimationFrameObjectByIndex(this.current.idx); + this.offset = frame.offset; + this.width = frame.width; + this.height = frame.height; + this._sourceAngle = frame.angle; + if (frame.anchorPoint) { + this.anchorPoint = frame.anchorPoint; + } + }, + + /** + * return the current animation frame index. + * @name getCurrentAnimationFrame + * @memberOf me.Sprite + * @function + * @return {Number} current animation frame index + */ + getCurrentAnimationFrame : function () { + return this.current.idx; + }, + + /** + * Returns the frame object by the index. + * @name getAnimationFrameObjectByIndex + * @memberOf me.Sprite + * @function + * @private + * @return {Number} if using number indices. Returns {Object} containing frame data if using texture atlas + */ + getAnimationFrameObjectByIndex : function (id) { + return this.current.frames[id]; + }, + + /** + * update the animation
+ * this is automatically called by the game manager {@link me.game} * @name update * @memberOf me.Sprite * @function * @protected - * @return false - **/ + * @param {Number} dt time since the last update in milliseconds. + */ update : function (dt) { + var result = false; + // Update animation if necessary + if (!this.animationpause && this.current && this.current.length > 1) { + var duration = this.getAnimationFrameObjectByIndex(this.current.idx).delay; + this.dt += dt; + while (this.dt >= duration) { + result = true; + this.dt -= duration; + this.setAnimationFrame(this.current.idx + 1); + + // Switch animation if we reach the end of the strip and a callback is defined + if (this.current.idx === 0 && this.resetAnim) { + // If string, change to the corresponding animation + if (typeof this.resetAnim === "string") { + this.setCurrentAnimation(this.resetAnim, null, true); + } + // Otherwise is must be callable + else if (this.resetAnim() === false) { + // Reset to last frame + this.setAnimationFrame(this.current.length - 1); + + // Bail early without skipping any more frames. + this.dt %= duration; + break; + } + } + // Get next frame duration + duration = this.getAnimationFrameObjectByIndex(this.current.idx).delay; + } + } + //update the "flickering" state if necessary - if (this.flickering) { - this.flickerDuration -= dt; - if (this.flickerDuration < 0) { - if (this.flickercb) { - this.flickercb(); + if (this._flicker.isFlickering) { + this._flicker.duration -= dt; + if (this._flicker.duration < 0) { + if (typeof (this._flicker.callback) === "function") { + this._flicker.callback(); } this.flicker(-1); } - return true; + result = true; } - return false; + + return result; }, /** @@ -256,9 +545,9 @@ **/ draw : function (renderer) { // do nothing if we are flickering - if (this.flickering) { - this.flickerState = !this.flickerState; - if (!this.flickerState) { + if (this._flicker.isFlickering) { + this._flicker.state = !this._flicker.state; + if (!this._flicker.state) { return; } } @@ -308,4 +597,7 @@ renderer.restore(); } }); + + // for backward compatiblity + me.AnimationSheet = me.Sprite; })(); diff --git a/tests/spec/init.js b/tests/spec/init.js index deb4d8709e..4f4fe90aed 100644 --- a/tests/spec/init.js +++ b/tests/spec/init.js @@ -1,3 +1,4 @@ describe("melonJS initialization", function () { me.boot(); + me.video.init(1024, 768, {wrapper : "screen"}); }); diff --git a/tests/spec/sprite-spec.js b/tests/spec/sprite-spec.js index 91553a1a2d..4e2d4c2316 100644 --- a/tests/spec/sprite-spec.js +++ b/tests/spec/sprite-spec.js @@ -1,9 +1,9 @@ describe("me.Sprite", function () { var container = new me.Container(50, 50, 150, 150); var sprite = new me.Sprite(0, 0, { - "width" : 32, - "height" : 32, - "image" : me.video.createCanvas(32, 32) + "framewidth" : 32, + "frameheight" : 32, + "image" : me.video.createCanvas(64, 64) }); // add to a parent container @@ -17,6 +17,10 @@ describe("me.Sprite", function () { expect(bounds.height).toEqual(32); }); + it("me.Sprite addAnimation should return the correct amount of frame", function () { + expect(sprite.addAnimation("test", [ 0, 1 ])).toEqual(2); + }); + // TODO: add bounds testing when transformation are added }); From 3679c0c0b96e3930187bdaada8b7c8da0296a9c1 Mon Sep 17 00:00:00 2001 From: Olivier Biot Date: Thu, 15 Sep 2016 11:02:24 +0800 Subject: [PATCH 108/177] Revert "Fix for getting parent node dimensions" --- src/video/video.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/video/video.js b/src/video/video.js index 84340f6ba7..f2e516ae5d 100644 --- a/src/video/video.js +++ b/src/video/video.js @@ -368,15 +368,9 @@ var parentNodeHeight; var parentNode = me.video.renderer.getScreenCanvas().parentNode; if (typeof (parentNode) !== "undefined") { - if (typeof parentNode.getBoundingClientRect === "function") { - var rect = parentNode.getBoundingClientRect(); - parentNodeWidth = rect.width || (rect.right - rect.left); - parentNodeHeight = rect.height || (rect.bottom - rect.top); - } else { - // for wrapper like Ejecta not supporting getBoundingClientRect() - parentNodeWidth = parentNode.width; - parentNodeHeight = parentNode.height; - } + // for cased where DOM is not implemented and so parentNode (e.g. Ejecta) + parentNodeWidth = parentNode.width; + parentNodeHeight = parentNode.height; } var _max_width = Math.min(maxWidth, parentNodeWidth || window.innerWidth); var _max_height = Math.min(maxHeight, parentNodeHeight || window.innerHeight); From e1f72842ed849d825e6b92b9384c24ca39e5b979 Mon Sep 17 00:00:00 2001 From: obiot Date: Thu, 15 Sep 2016 10:59:24 +0800 Subject: [PATCH 109/177] [#777] minor documentation update --- src/renderable/sprite.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderable/sprite.js b/src/renderable/sprite.js index 817b694326..5c3a8392eb 100644 --- a/src/renderable/sprite.js +++ b/src/renderable/sprite.js @@ -7,7 +7,7 @@ (function () { /** - * A object to display a fixed or animated sprite on screen. + * An object to display a fixed or animated sprite on screen. * @class * @extends me.Sprite * @memberOf me @@ -20,7 +20,7 @@ * @param {Number} [settings.frameheight] Height of a single frame within the spritesheet * @param {me.Vector2d} [settings.anchorPoint={x:0.5, y:0.5}] Anchor point to draw the frame at (defaults to the center of the frame). * @example - * // standalone image, with anchor in the center + * // create a standalone sprite, with anchor in the center * var sprite = new me.Sprite(0, 0, { * image : "PlayerTexture", * framewidth : 64, From ca6ee526827ba204dd1a45f01a271e7a80c45593 Mon Sep 17 00:00:00 2001 From: obiot Date: Thu, 15 Sep 2016 16:28:20 +0800 Subject: [PATCH 110/177] [documentation] fixed `me.Renderable.pos` definition (@krojew) --- src/renderable/renderable.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/renderable/renderable.js b/src/renderable/renderable.js index 3dd5ae9310..610ae902a6 100644 --- a/src/renderable/renderable.js +++ b/src/renderable/renderable.js @@ -199,9 +199,14 @@ this._absPos = new me.Vector2d(x, y); } - // set position to observable. Can use updateBounds, as _bounds using a regular vector. - // will not lead to stack too deep. - if (this.pos) { + /** + * Position of the Renderable relative to its parent container + * @public + * @type {me.ObservableVector3d} + * @name pos + * @memberOf me.Renderable + */ + if (this.pos instanceof me.ObservableVector3d) { this.pos.setMuted(x, y, 0).setCallback(this.updateBoundsPos.bind(this)); } else { this.pos = new me.ObservableVector3d(x, y, 0, { onUpdate: this.updateBoundsPos.bind(this) }); From 0a51e8a7a91d8cb9bc98c468e38c93fa30d4ae69 Mon Sep 17 00:00:00 2001 From: Olivier Biot Date: Mon, 19 Sep 2016 10:11:04 +0800 Subject: [PATCH 111/177] [#815] further clean `me.Sprite`, better API consistency also managed to remove one more property from me.Sprite, and aligned the scale method with the one from polygon, they were missing the return values (@see #806) --- src/renderable/renderable.js | 32 ++++++++++++++++++++- src/renderable/sprite.js | 54 +++++++++--------------------------- 2 files changed, 44 insertions(+), 42 deletions(-) diff --git a/src/renderable/renderable.js b/src/renderable/renderable.js index 610ae902a6..3d031076ce 100644 --- a/src/renderable/renderable.js +++ b/src/renderable/renderable.js @@ -267,13 +267,43 @@ * @see me.Renderable#currentTransform * @function * @param {me.Matrix2d} matrix the transformation matrix - * @return {me.Polygon} Reference to this object for method chaining + * @return {me.Renderable} Reference to this object for method chaining */ transform : function (m) { this.currentTransform.multiply(m); return this; }, + /** + * scale the sprite around his anchor point + * @name scale + * @memberOf me.Renderable + * @function + * @param {Number} x x scaling ratio + * @param {Number} y y scaling ratio + * @return {me.Renderable} Reference to this object for method chaining + */ + scale : function (x, y) { + // set the scaleFlag + this.currentTransform.scale(x, y); + // resize the bounding box + this.getBounds().resize(this.width * x, this.height * y); + return this; + }, + + /** + * scale the sprite around his anchor point + * @name scaleV + * @memberOf me.Renderable + * @function + * @param {me.Vector2d} vector ratio + * @return {me.Renderable} Reference to this object for method chaining + */ + scaleV : function (ratio) { + this.scale(ratio.x, ratio.y); + return this; + }, + /** * update function * called by the game manager on each game loop diff --git a/src/renderable/sprite.js b/src/renderable/sprite.js index 5c3a8392eb..2e87628fd0 100644 --- a/src/renderable/sprite.js +++ b/src/renderable/sprite.js @@ -81,15 +81,17 @@ // animation frame delta this.dt = 0; - // just to keep track of when we flip - this.lastflipX = false; - this.lastflipY = false; + // keep track of when we flip + this._flip = { + lastX : false, + lastY : false + }; if (typeof (settings.flipX) !== "undefined") { - this.flipX(!!settings.flipX); + this._flip.lastX(!!settings.flipX); } if (typeof (settings.flipY) !== "undefined") { - this.flipY(!!settings.flipY); + this._flip.lastY(!!settings.flipY); } // flicker settings @@ -211,9 +213,9 @@ * @param {Boolean} flip enable/disable flip */ flipX : function (flip) { - console.warn("Deprecated: me.Sprite.flipX"); - if (flip !== this.lastflipX) { - this.lastflipX = flip; + if (flip !== this._flip.lastX) { + console.warn("Deprecated: me.Sprite.flipX"); + this._flip.lastX = flip; // invert the scale.x value this.currentTransform.scaleX(-1); } @@ -227,44 +229,14 @@ * @param {Boolean} flip enable/disable flip */ flipY : function (flip) { - console.warn("Deprecated: me.Sprite.flipY"); - if (flip !== this.lastflipY) { - this.lastflipY = flip; + if (flip !== this._flip.lastY) { + console.warn("Deprecated: me.Sprite.flipY"); + this._flip.lastY = flip; // invert the scale.x value this.currentTransform.scaleY(-1); } }, - /** - * scale the sprite around his center
- * @name scale - * @memberOf me.Sprite - * @function - * @param {Number} x x scaling ratio - * @param {Number} y y scaling ratio - */ - scale : function (x, y) { - console.warn("Deprecated: me.Sprite.scale"); - - // set the scaleFlag - this.currentTransform.scale(x, y); - - // resize the bounding box - this.getBounds().resize(this.width * x, this.height * y); - }, - - /** - * scale the sprite around his center
- * @name scaleV - * @memberOf me.Sprite - * @function - * @param {me.Vector2d} vector ratio - */ - scaleV : function (ratio) { - console.warn("Deprecated: me.Sprite.scaleV"); - this.scale(ratio.x, ratio.y); - }, - /** * add an animation
* For fixed-sized cell sprite sheet, the index list must follow the From 88bfb729c5095d70787eab72d7cf23daf627d7f8 Mon Sep 17 00:00:00 2001 From: Olivier Biot Date: Mon, 19 Sep 2016 10:20:50 +0800 Subject: [PATCH 112/177] documentation fix/update --- src/renderable/renderable.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/renderable/renderable.js b/src/renderable/renderable.js index 3d031076ce..ecaf1293c6 100644 --- a/src/renderable/renderable.js +++ b/src/renderable/renderable.js @@ -275,12 +275,12 @@ }, /** - * scale the sprite around his anchor point + * scale the renderable around his anchor point * @name scale * @memberOf me.Renderable * @function - * @param {Number} x x scaling ratio - * @param {Number} y y scaling ratio + * @param {Number} x a number representing the abscissa of the scaling vector. + * @param {Number} y a number representing the ordinate of the scaling vector. * @return {me.Renderable} Reference to this object for method chaining */ scale : function (x, y) { @@ -292,15 +292,15 @@ }, /** - * scale the sprite around his anchor point + * scale the renderable around his anchor point * @name scaleV * @memberOf me.Renderable * @function - * @param {me.Vector2d} vector ratio + * @param {me.Vector2d} vector scaling vector * @return {me.Renderable} Reference to this object for method chaining */ - scaleV : function (ratio) { - this.scale(ratio.x, ratio.y); + scaleV : function (v) { + this.scale(v.x, v.y); return this; }, From bf101aa6c403e0dd5f77e8dbdec8614829922f82 Mon Sep 17 00:00:00 2001 From: obiot Date: Mon, 19 Sep 2016 12:14:59 +0800 Subject: [PATCH 113/177] fix `me.Rect` `width` & `height` values *after* any transformations have been applied --- src/shapes/rectangle.js | 31 +++++++++++++++++++++++-------- tests/spec/rectangle-spec.js | 7 ++++++- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/shapes/rectangle.js b/src/shapes/rectangle.js index 875940b567..68270c772e 100644 --- a/src/shapes/rectangle.js +++ b/src/shapes/rectangle.js @@ -29,7 +29,7 @@ new me.Vector2d(), new me.Vector2d(), new me.Vector2d(), new me.Vector2d() ]; - + this.shapeType = "Polygon"; this.setShape(x, y, w, h); }, @@ -47,7 +47,7 @@ */ setShape : function (x, y, w, h) { - this.points[0].set(0, 0); // 0, 0 + this.points[0].set(0, 0); // 0, 0 this.points[1].set(w, 0); // 1, 0 this.points[2].set(w, h); // 1, 1 this.points[3].set(0, h); // 0, 1 @@ -87,6 +87,21 @@ return this; }, + /** + * Computes the calculated collision polygon. + * This **must** be called if the `points` array is modified manually. + * @ignore + * @name recalc + * @memberOf me.Rect + * @function + */ + recalc : function () { + this._super(me.Polygon, "recalc"); + this._width = this.points[2].x; + this._height = this.points[2].y; + return this; + }, + /** * update the bounding box for this shape. * @name updateBounds @@ -237,7 +252,7 @@ }); // redefine some properties to ease our life when getting the rectangle coordinates - + /** * left coordinate of the Rectangle * @public @@ -295,7 +310,7 @@ }, configurable : true }); - + /** * width of the Rectangle * @public @@ -308,8 +323,8 @@ return this._width; }, set : function (value) { - this._width = value; this.points[1].x = this.points[2].x = value; + // _height updated in recalc this.recalc(); }, configurable : true @@ -327,11 +342,11 @@ return this._height; }, set : function (value) { - this._height = value; this.points[2].y = this.points[3].y = value; + // _height updated in recalc this.recalc(); }, configurable : true }); - -})(); \ No newline at end of file + +})(); diff --git a/tests/spec/rectangle-spec.js b/tests/spec/rectangle-spec.js index 097faf65c2..bf05266720 100644 --- a/tests/spec/rectangle-spec.js +++ b/tests/spec/rectangle-spec.js @@ -1,6 +1,6 @@ describe("Shape : me.Rect", function () { - var rect1 = new me.Rect(0, 0, 100, 100); + var rect1 = new me.Rect(0, 0, 50, 50); // rect 2 overlap rect 1 var rect2 = new me.Rect(50, 50, 100, 100); // rect 3 contains rect 1 and rect 2 @@ -11,6 +11,11 @@ describe("Shape : me.Rect", function () { var rect5 = rect2.clone().union(rect4); describe("rect1", function () { + it("scale rect1", function () { + rect1.scale(2, 2); + expect(rect1.width).toEqual(100); + expect(rect1.height).toEqual(100); + }); it("rect 1 overlaps rect2", function () { expect(rect1.overlaps(rect2)).toEqual(true); }); From 874647b53aec9b509b1c2acaf4400166920e7f25 Mon Sep 17 00:00:00 2001 From: obiot Date: Mon, 19 Sep 2016 17:27:53 +0800 Subject: [PATCH 114/177] code simplification following https://github.com/melonjs/melonJS/commit/bf101aa6c403e0dd5f77e8dbdec8614829922f82 --- src/renderable/renderable.js | 8 ++++---- src/shapes/rectangle.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/renderable/renderable.js b/src/renderable/renderable.js index ecaf1293c6..821bf66989 100644 --- a/src/renderable/renderable.js +++ b/src/renderable/renderable.js @@ -384,7 +384,7 @@ }); /** - * width of the Renderable bounding box
+ * width of the Renderable bounding box * @public * @type {Number} * @name width @@ -395,14 +395,14 @@ return this._width; }, set : function (value) { - this.getBounds().resize(value, this._height); + this.getBounds().width = value; this._width = value; }, configurable : true }); /** - * height of the Renderable bounding box
+ * height of the Renderable bounding box * @public * @type {Number} * @name height @@ -413,7 +413,7 @@ return this._height; }, set : function (value) { - this.getBounds().resize(this._width, value); + this.getBounds().height = value; this._height = value; }, configurable : true diff --git a/src/shapes/rectangle.js b/src/shapes/rectangle.js index 68270c772e..af37d764bd 100644 --- a/src/shapes/rectangle.js +++ b/src/shapes/rectangle.js @@ -324,7 +324,7 @@ }, set : function (value) { this.points[1].x = this.points[2].x = value; - // _height updated in recalc + // _width updated in recalc this.recalc(); }, configurable : true From 75091fe147dc1f750a7bee928d01a41cdd6823a0 Mon Sep 17 00:00:00 2001 From: obiot Date: Mon, 19 Sep 2016 17:38:18 +0800 Subject: [PATCH 115/177] better test case that change properties separately --- tests/spec/rectangle-spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/spec/rectangle-spec.js b/tests/spec/rectangle-spec.js index bf05266720..4c1738e283 100644 --- a/tests/spec/rectangle-spec.js +++ b/tests/spec/rectangle-spec.js @@ -1,6 +1,6 @@ describe("Shape : me.Rect", function () { - var rect1 = new me.Rect(0, 0, 50, 50); + var rect1 = new me.Rect(0, 0, 25, 50); // rect 2 overlap rect 1 var rect2 = new me.Rect(50, 50, 100, 100); // rect 3 contains rect 1 and rect 2 @@ -12,7 +12,7 @@ describe("Shape : me.Rect", function () { describe("rect1", function () { it("scale rect1", function () { - rect1.scale(2, 2); + rect1.scale(4, 2); expect(rect1.width).toEqual(100); expect(rect1.height).toEqual(100); }); From 7c8d276a509c752047f46fb2a6f84a37eb0559af Mon Sep 17 00:00:00 2001 From: obiot Date: Mon, 19 Sep 2016 17:44:47 +0800 Subject: [PATCH 116/177] [#777] remove internal references to me.AnimationSheet --- src/entity/entity.js | 2 +- src/level/TMXTile.js | 2 +- src/video/canvas/texture.js | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/entity/entity.js b/src/entity/entity.js index c82df6ecba..c90dba0ba2 100644 --- a/src/entity/entity.js +++ b/src/entity/entity.js @@ -54,7 +54,7 @@ settings.height]); if (settings.image) { - this.renderable = new me.AnimationSheet(0, 0, { + this.renderable = new me.Sprite(0, 0, { "image" : settings.image, "framewidth" : ~~(settings.framewidth || settings.width), "frameheight" : ~~(settings.frameheight || settings.height), diff --git a/src/level/TMXTile.js b/src/level/TMXTile.js index 3c34f9acfa..04ac020c1e 100644 --- a/src/level/TMXTile.js +++ b/src/level/TMXTile.js @@ -139,7 +139,7 @@ * @public * @function * @param {Object} [settings] see {@link me.Sprite} - * @return {me.Renderable} either a me.Sprite object or a me.AnimationSheet (for animated tiles) + * @return {me.Renderable} a me.Sprite object */ getRenderable : function (settings) { var renderable; diff --git a/src/video/canvas/texture.js b/src/video/canvas/texture.js index e3fbe5b44d..1a425dc610 100644 --- a/src/video/canvas/texture.js +++ b/src/video/canvas/texture.js @@ -287,8 +287,8 @@ * @function * @param {String[]|Number[]} names list of names for each sprite * (when manually creating a Texture out of a spritesheet, only numeric values are authorized) - * @param {Object} [settings] Additional settings passed to the {@link me.AnimationSheet} contructor - * @return {me.AnimationSheet} + * @param {Object} [settings] Additional settings passed to the {@link me.Sprite} contructor + * @return {me.Sprite} * @example * // create a new texture atlas object under the `game` namespace * game.texture = new me.video.renderer.Texture( @@ -296,7 +296,7 @@ * me.loader.getImage("texture") * ); * - * // create a new animationSheet as renderable for the entity + * // create a new Sprite as renderable for the entity * this.renderable = game.texture.createAnimationFromName([ * "walk0001.png", "walk0002.png", "walk0003.png", * "walk0004.png", "walk0005.png", "walk0006.png", @@ -326,7 +326,7 @@ } } // instantiate a new animation sheet object - return new me.AnimationSheet(0, 0, Object.assign({ + return new me.Sprite(0, 0, Object.assign({ image: this, framewidth: 0, frameheight: 0, From 87ac6da5445d7702a298cb8aa3aeb532bb051afa Mon Sep 17 00:00:00 2001 From: obiot Date: Mon, 19 Sep 2016 17:48:16 +0800 Subject: [PATCH 117/177] [#765] apply the same transformation to the bounding box does not work quite well though, as the bounding box is resetted to its original size when the object is moved .... --- src/renderable/renderable.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/renderable/renderable.js b/src/renderable/renderable.js index 821bf66989..e7575d868e 100644 --- a/src/renderable/renderable.js +++ b/src/renderable/renderable.js @@ -271,6 +271,7 @@ */ transform : function (m) { this.currentTransform.multiply(m); + this.getBounds().transform(m); return this; }, From 2d25ba915622a096bbf678b24bb5b61009058d48 Mon Sep 17 00:00:00 2001 From: obiot Date: Tue, 20 Sep 2016 13:22:39 +0800 Subject: [PATCH 118/177] [#765] use the cached frame size when rendering a sprite, instead of relying on the me.Sprite object (bounds) size This allows to freely resize the sprite bounds together with transformations applied to to a Sprite itself. Also me.Rect properties are now set to the biggest frame size (if there is an animation) --- plugins/debug/debugPanel.js | 2 +- src/renderable/sprite.js | 74 +++++++++++++++++++++++-------------- src/video/canvas/texture.js | 17 ++++++--- 3 files changed, 60 insertions(+), 33 deletions(-) diff --git a/plugins/debug/debugPanel.js b/plugins/debug/debugPanel.js index 15b755cb61..07eace3c55 100644 --- a/plugins/debug/debugPanel.js +++ b/plugins/debug/debugPanel.js @@ -210,7 +210,7 @@ renderer.save(); renderer.setColor("green"); renderer.translate(x, y); - renderer.strokeRect(this.left, this.top, this.width, this.height); + renderer.drawShape(this.getBounds()); renderer.restore(); } }); diff --git a/src/renderable/sprite.js b/src/renderable/sprite.js index 2e87628fd0..8439cf2979 100644 --- a/src/renderable/sprite.js +++ b/src/renderable/sprite.js @@ -52,16 +52,6 @@ */ this.animationspeed = 100; - // current frame texture offset - /** - * The position to draw from on the source image. - * @public - * @type me.Vector2d - * @name offset - * @memberOf me.Vector2d - */ - this.offset = new me.Vector2d(); - /** * Source rotation angle for pre-rotating the source image
* Commonly used for TexturePacker @@ -75,8 +65,15 @@ // a flag to reset animation this.resetAnim = null; - // default animation sequence - this.current = null; + // current frame information + // (reusing current, any better/cleaner place?) + this.current = { + //current frame texture offset + offset : new me.Vector2d(), + // current frame size + width : 0, + height : 0 + }; // animation frame delta this.dt = 0; @@ -117,7 +114,7 @@ var region = settings.image.getRegion(settings.region); if (region) { // set the sprite offset within the texture - this.offset.setV(region.offset); + this.current.offset.setV(region.offset); // set angle if defined this._sourceAngle = region.angle; settings.framewidth = settings.framewidth || region.width; @@ -134,6 +131,9 @@ settings.frameheight = settings.frameheight || this.image.height; this.textureAtlas = me.video.renderer.cache.get(this.image, settings).getAtlas(); } + // update the default "current" size + this.current.width = settings.framewidth; + this.current.height = settings.frameheight; // store/reset the current atlas information if specified if (typeof(settings.atlas) !== "undefined") { @@ -146,8 +146,8 @@ // call the super constructor this._super(me.Renderable, "init", [ x, y, - settings.framewidth || this.image.width, - settings.frameheight || this.image.height + this.current.width, + this.current.height ]); // set the default rotation angle is defined in the settings @@ -417,9 +417,9 @@ setAnimationFrame : function (idx) { this.current.idx = (idx || 0) % this.current.length; var frame = this.getAnimationFrameObjectByIndex(this.current.idx); - this.offset = frame.offset; - this.width = frame.width; - this.height = frame.height; + this.current.offset = frame.offset; + this.current.width = frame.width; + this.current.height = frame.height; this._sourceAngle = frame.angle; if (frame.anchorPoint) { this.anchorPoint = frame.anchorPoint; @@ -528,8 +528,10 @@ var xpos = ~~this.pos.x, ypos = ~~this.pos.y; - var w = this.width, - h = this.height; + var w = this.current.width, + h = this.current.height; + + var offset = this.current.offset; // save context renderer.save(); @@ -552,17 +554,17 @@ if (this._sourceAngle !== 0) { renderer.translate(-xpos, -ypos); renderer.rotate(this._sourceAngle); - xpos -= this.height; - w = this.height; - h = this.width; + xpos -= h; + w = this.current.height; + h = this.current.width; } renderer.drawImage( this.image, - this.offset.x, this.offset.y, // sx,sy - w, h, // sw,sh - xpos, ypos, // dx,dy - w, h // dw,dh + offset.x, offset.y, // sx,sy + w, h, // sw,sh + xpos, ypos, // dx,dy + w, h // dw,dh ); // restore context @@ -570,6 +572,24 @@ } }); + /** + * The position to draw from on the source image. + * @public + * @type {me.Vector2d} + * @name offset + * @memberOf me.Sprite + */ + Object.defineProperty(me.Sprite.prototype, "offset", { + /* for backward compatiblity */ + get : function () { + return this.current.offset; + }, + set : function (value) { + this.current.offset.setV(value); + }, + configurable : true + }); + // for backward compatiblity me.AnimationSheet = me.Sprite; })(); diff --git a/src/video/canvas/texture.js b/src/video/canvas/texture.js index 1a425dc610..a275cef038 100644 --- a/src/video/canvas/texture.js +++ b/src/video/canvas/texture.js @@ -315,21 +315,28 @@ */ createAnimationFromName : function (names, settings) { var tpAtlas = [], indices = {}; + var width = 0, height = 0; + var region; // iterate through the given names // and create a "normalized" atlas for (var i = 0; i < names.length;++i) { - tpAtlas[i] = this.getRegion(names[i]); - indices[names[i]] = i; - if (tpAtlas[i] == null) { + region = this.getRegion(names[i]); + if (region == null) { // throw an error throw new me.video.renderer.Texture.Error("Texture - region for " + names[i] + " not found"); } + tpAtlas[i] = region; + // save the corresponding index + indices[names[i]] = i; + // calculate the max size of a frame + width = Math.max(region.width, width); + height = Math.max(region.height, height); } // instantiate a new animation sheet object return new me.Sprite(0, 0, Object.assign({ image: this, - framewidth: 0, - frameheight: 0, + framewidth: width, + frameheight: height, margin: 0, spacing: 0, atlas: tpAtlas, From 5e952f47950f1a2d057a553d92ea69bfb889c7cf Mon Sep 17 00:00:00 2001 From: obiot Date: Tue, 20 Sep 2016 13:29:04 +0800 Subject: [PATCH 119/177] [#777] remove leftover references of me.AnimationSheet from the examples --- examples/isometric_rpg/js/entities/entities.js | 2 +- examples/whack-a-mole/js/entities/entities.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/isometric_rpg/js/entities/entities.js b/examples/isometric_rpg/js/entities/entities.js index d2f8b41a59..5ae1389717 100644 --- a/examples/isometric_rpg/js/entities/entities.js +++ b/examples/isometric_rpg/js/entities/entities.js @@ -31,7 +31,7 @@ game.PlayerEntity = me.Entity.extend({ me.loader.getImage("Blank_Sprite_Sheet_4_2_by_KnightYamato") ); - // create a new animationSheet object + // create a new sprite object this.renderable = texture.createAnimationFromName([0, 1, 2, 3, 4, 5, 6, 7, 8]); // define an additional basic walking animation this.renderable.addAnimation ("simple_walk", [0,1,2]); diff --git a/examples/whack-a-mole/js/entities/entities.js b/examples/whack-a-mole/js/entities/entities.js index 1ad0ed4cde..dda264460e 100644 --- a/examples/whack-a-mole/js/entities/entities.js +++ b/examples/whack-a-mole/js/entities/entities.js @@ -2,11 +2,11 @@ * a mole entity * note : we don"t use EntityObject, since we wont" use regular collision, etc.. */ -game.MoleEntity = me.AnimationSheet.extend( +game.MoleEntity = me.Sprite.extend( { init:function (x, y) { // call the constructor - this._super(me.AnimationSheet, "init", [x, y , { image: me.loader.getImage("mole"), framewidth: 178, frameheight: 140}]); + this._super(me.Sprite, "init", [x, y , { image: me.loader.getImage("mole"), framewidth: 178, frameheight: 140}]); // idle animation this.addAnimation ("idle", [0]); From 9c3b8767e43a992e1a0f2b5a33a73c80d6834549 Mon Sep 17 00:00:00 2001 From: obiot Date: Wed, 21 Sep 2016 10:51:06 +0800 Subject: [PATCH 120/177] updated and simpler Object.assign polyfill from MDN --- src/lang/object.js | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/src/lang/object.js b/src/lang/object.js index 343d11c922..e4c503b8ea 100644 --- a/src/lang/object.js +++ b/src/lang/object.js @@ -113,7 +113,7 @@ if (!Object.assign) { * @param {Object} target The target object. * @param {Object[]} sources The source object(s). * @return {Object} The target object gets returned. - * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign|Object.assign} + * @see {@link https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign} * @example * // Merging objects * var o1 = { a: 1 }; @@ -125,33 +125,28 @@ if (!Object.assign) { * // { a: 1, b: 2, c: 3 } */ - Object.defineProperty(Object, "assign", { - enumerable: false, - configurable: true, - writable: true, - value: function (target) { + (function () { + Object.assign = function (target) { "use strict"; + // We must check against these specific cases. if (target === undefined || target === null) { - throw new TypeError("Cannot convert first argument to object"); + throw new TypeError("Cannot convert undefined or null to object"); } - var to = Object(target); - for (var i = 1; i < arguments.length; i++) { - var nextSource = arguments[i]; - if (nextSource === undefined || nextSource === null) { - continue; - } - var keysArray = Object.keys(Object(nextSource)); - for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) { - var nextKey = keysArray[nextIndex]; - var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey); - if (desc !== undefined && desc.enumerable) { - to[nextKey] = nextSource[nextKey]; + + var output = Object(target); + for (var index = 1; index < arguments.length; index++) { + var source = arguments[index]; + if (source !== undefined && source !== null) { + for (var nextKey in source) { + if (source.hasOwnProperty(nextKey)) { + output[nextKey] = source[nextKey]; + } } } - } - return to; - } - }); + } + return output; + }; + })(); } /** From 7582d7060ae9a4daca20c4cd7fa5471cea6c3954 Mon Sep 17 00:00:00 2001 From: obiot Date: Wed, 21 Sep 2016 11:13:14 +0800 Subject: [PATCH 121/177] also tuck the frame `angle` property into the `current` object that also allow to reuse the `angle` property, instead of defining another `_sourceAngle` one in me.Sprite --- src/renderable/sprite.js | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/renderable/sprite.js b/src/renderable/sprite.js index 8439cf2979..58f0cb88db 100644 --- a/src/renderable/sprite.js +++ b/src/renderable/sprite.js @@ -52,13 +52,6 @@ */ this.animationspeed = 100; - /** - * Source rotation angle for pre-rotating the source image
- * Commonly used for TexturePacker - * @ignore - */ - this._sourceAngle = 0; - // hold all defined animation this.anim = {}; @@ -72,7 +65,9 @@ offset : new me.Vector2d(), // current frame size width : 0, - height : 0 + height : 0, + // Source rotation angle for pre-rotating the source image + angle : 0 }; // animation frame delta @@ -116,7 +111,7 @@ // set the sprite offset within the texture this.current.offset.setV(region.offset); // set angle if defined - this._sourceAngle = region.angle; + this.current.angle = region.angle; settings.framewidth = settings.framewidth || region.width; settings.frameheight = settings.frameheight || region.height; } else { @@ -417,12 +412,11 @@ setAnimationFrame : function (idx) { this.current.idx = (idx || 0) % this.current.length; var frame = this.getAnimationFrameObjectByIndex(this.current.idx); - this.current.offset = frame.offset; - this.current.width = frame.width; - this.current.height = frame.height; - this._sourceAngle = frame.angle; + // copy all properties of the current frame into current + Object.assign(this.current, frame); + // set global anchortPoint if defined if (frame.anchorPoint) { - this.anchorPoint = frame.anchorPoint; + this.anchorPoint.setV(frame.anchorPoint); } }, @@ -524,14 +518,17 @@ } } + // the frame to draw + var frame = this.current; + // clamp position vector to pixel grid var xpos = ~~this.pos.x, ypos = ~~this.pos.y; - var w = this.current.width, - h = this.current.height; + var w = frame.width, + h = frame.height; - var offset = this.current.offset; + var offset = frame.offset; // save context renderer.save(); @@ -551,12 +548,12 @@ ); // remove image's TexturePacker/ShoeBox rotation - if (this._sourceAngle !== 0) { + if (frame.angle !== 0) { renderer.translate(-xpos, -ypos); - renderer.rotate(this._sourceAngle); + renderer.rotate(frame.angle); xpos -= h; - w = this.current.height; - h = this.current.width; + w = frame.height; + h = frame.width; } renderer.drawImage( From 8d98ba2ad6302258660d89bf5252d313573a135d Mon Sep 17 00:00:00 2001 From: obiot Date: Wed, 21 Sep 2016 11:36:00 +0800 Subject: [PATCH 122/177] [#765] fix shape drawing for polygon based rectangle --- src/video/canvas/canvas_renderer.js | 2 +- src/video/webgl/webgl_renderer.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video/canvas/canvas_renderer.js b/src/video/canvas/canvas_renderer.js index 38454558f0..f59e35af40 100644 --- a/src/video/canvas/canvas_renderer.js +++ b/src/video/canvas/canvas_renderer.js @@ -517,7 +517,7 @@ * @param {me.Rect|me.Polygon|me.Line|me.Ellipse} shape a shape object */ drawShape : function (shape) { - if (shape instanceof me.Rect) { + if (shape.shapeType === "Rectangle") { this.strokeRect(shape.left, shape.top, shape.width, shape.height); } else if (shape instanceof me.Line || shape instanceof me.Polygon) { this.strokePolygon(shape); diff --git a/src/video/webgl/webgl_renderer.js b/src/video/webgl/webgl_renderer.js index f4fbe32769..3e7a8c5b03 100644 --- a/src/video/webgl/webgl_renderer.js +++ b/src/video/webgl/webgl_renderer.js @@ -653,7 +653,7 @@ * @param {me.Rect|me.Polygon|me.Line|me.Ellipse} shape a shape object */ drawShape : function (shape) { - if (shape instanceof me.Rect) { + if (shape.shapeType === "Rectangle") { this.strokeRect(shape.left, shape.top, shape.width, shape.height); } else if (shape instanceof me.Line || shape instanceof me.Polygon) { this.save(); From 62df6deb2006d6399a5a3418fd15af25277c8b75 Mon Sep 17 00:00:00 2001 From: obiot Date: Wed, 21 Sep 2016 12:00:34 +0800 Subject: [PATCH 123/177] W3C PointerEvent detection. This property has been deprecated in the latest version of the W3C Pointer Events specification. https://msdn.microsoft.com/en-us/library/windows/apps/hh972607.aspx --- src/input/pointer.js | 6 +++--- src/system/device.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/input/pointer.js b/src/input/pointer.js index b9675c64bd..f5484c1b5f 100644 --- a/src/input/pointer.js +++ b/src/input/pointer.js @@ -207,10 +207,10 @@ ), false); // check standard - if (navigator.pointerEnabled) { + if (window.PointerEvent) { activeEventList = pointerEventList; } - else if (navigator.msPointerEnabled) { // check for backward compatibility with the 'MS' prefix + else if (window.MSPointerEvent) { // check for backward compatibility with the 'MS' prefix activeEventList = MSPointerEventList; } else if (me.device.touch && me.device.isMobile) { // `touch****` events for iOS/Android devices @@ -300,7 +300,7 @@ } // if PointerEvent is not supported - if (!me.device.pointerEnabled) { + if (!me.device.pointerEvent) { // -> define pointerId to simulate the PointerEvent standard e.pointerId = changedTouch.id; } diff --git a/src/system/device.js b/src/system/device.js index f376695644..9673616bac 100644 --- a/src/system/device.js +++ b/src/system/device.js @@ -38,13 +38,13 @@ } // future proofing (MS) feature detection - me.device.pointerEnabled = me.agent.prefixed("pointerEnabled", navigator); + me.device.pointerEvent = me.agent.prefixed("PointerEvent", window); me.device.maxTouchPoints = me.agent.prefixed("maxTouchPoints", navigator) || 0; window.gesture = me.agent.prefixed("gesture"); // detect touch capabilities me.device.touch = ("createTouch" in document) || ("ontouchstart" in window) || - (me.device.cocoon) || (me.device.pointerEnabled && (me.device.maxTouchPoints > 0)); + (me.device.cocoon) || (me.device.pointerEvent && (me.device.maxTouchPoints > 0)); // accelerometer detection me.device.hasAccelerometer = ( From 7b6aaa769dbaac9310fc1c406a6f2d5ec26f129d Mon Sep 17 00:00:00 2001 From: obiot Date: Wed, 21 Sep 2016 13:32:17 +0800 Subject: [PATCH 124/177] me.Rect is not of polygon type ! well mathematically speaking it is, but here it's just a box that's how it should be, especially if we do replace this class with a much more simple version "box" one day --- src/shapes/rectangle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shapes/rectangle.js b/src/shapes/rectangle.js index af37d764bd..35fa943542 100644 --- a/src/shapes/rectangle.js +++ b/src/shapes/rectangle.js @@ -30,7 +30,7 @@ new me.Vector2d(), new me.Vector2d() ]; - this.shapeType = "Polygon"; + this.shapeType = "Rectangle"; this.setShape(x, y, w, h); }, From 63040c15481162a1263df0938004be91a8ff0a9b Mon Sep 17 00:00:00 2001 From: obiot Date: Wed, 21 Sep 2016 13:56:07 +0800 Subject: [PATCH 125/177] cleaner way (API-wise) to create a rectangle out of any shape `setPoints` might not be the best name though, feel free to suggest anything else :) --- src/shapes/poly.js | 16 ++++------------ src/shapes/rectangle.js | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/shapes/poly.js b/src/shapes/poly.js index ed11368134..ba6b287542 100644 --- a/src/shapes/poly.js +++ b/src/shapes/poly.js @@ -272,21 +272,13 @@ * @return {me.Rect} this shape bounding box Rectangle object */ updateBounds : function () { - var x = Infinity, y = Infinity, right = -Infinity, bottom = -Infinity; - this.points.forEach(function (point) { - x = Math.min(x, point.x); - y = Math.min(y, point.y); - right = Math.max(right, point.x); - bottom = Math.max(bottom, point.y); - }); - if (!this._bounds) { - this._bounds = new me.Rect(x, y, right - x, bottom - y); - } else { - this._bounds.setShape(x, y, right - x, bottom - y); + this._bounds = new me.Rect(0, 0, 0, 0); } + this._bounds.setPoints(this.points); + this._bounds.translateV(this.pos); - return this._bounds.translateV(this.pos); + return this._bounds; }, /** diff --git a/src/shapes/rectangle.js b/src/shapes/rectangle.js index 35fa943542..cdf04e5ba4 100644 --- a/src/shapes/rectangle.js +++ b/src/shapes/rectangle.js @@ -87,6 +87,25 @@ return this; }, + /** + * resize the rectangle to contain all the given points coordinates. + * @name setPoints + * @memberOf me.Rect + * @function + * @param {me.Vector2d[]} points array of vector defining a shape + * @return {me.Rect} this shape bounding box Rectangle object + */ + setPoints : function (points) { + var x = Infinity, y = Infinity, right = -Infinity, bottom = -Infinity; + points.forEach(function (point) { + x = Math.min(x, point.x); + y = Math.min(y, point.y); + right = Math.max(right, point.x); + bottom = Math.max(bottom, point.y); + }); + this.setShape(x, y, right - x, bottom - y); + }, + /** * Computes the calculated collision polygon. * This **must** be called if the `points` array is modified manually. From 20c4d601ae647e98484906158ef9705caf5cbd10 Mon Sep 17 00:00:00 2001 From: obiot Date: Wed, 21 Sep 2016 13:58:27 +0800 Subject: [PATCH 126/177] forgot to return this ! --- src/shapes/rectangle.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/shapes/rectangle.js b/src/shapes/rectangle.js index cdf04e5ba4..2d17b2da6d 100644 --- a/src/shapes/rectangle.js +++ b/src/shapes/rectangle.js @@ -104,6 +104,7 @@ bottom = Math.max(bottom, point.y); }); this.setShape(x, y, right - x, bottom - y); + return this; }, /** From 51f6817a0c07cd34f255a97fd76ac046f44477df Mon Sep 17 00:00:00 2001 From: obiot Date: Wed, 21 Sep 2016 14:53:55 +0800 Subject: [PATCH 127/177] [#765] getting there... still not correctly aligned though --- src/renderable/renderable.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/renderable/renderable.js b/src/renderable/renderable.js index e7575d868e..80b8ff7879 100644 --- a/src/renderable/renderable.js +++ b/src/renderable/renderable.js @@ -270,8 +270,10 @@ * @return {me.Renderable} Reference to this object for method chaining */ transform : function (m) { + var bounds = this.getBounds(); this.currentTransform.multiply(m); - this.getBounds().transform(m); + bounds.setPoints(bounds.transform(m).points); + bounds.pos.setV(this.pos); return this; }, From 758018178ce26966150779f8ab21e7473637baea Mon Sep 17 00:00:00 2001 From: obiot Date: Thu, 22 Sep 2016 14:24:00 +0800 Subject: [PATCH 128/177] [#765] fix hitbox rendering position the actual coordinates are correct, just the rendering (in debug mode) was off actually --- plugins/debug/debugPanel.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/debug/debugPanel.js b/plugins/debug/debugPanel.js index 07eace3c55..1dc75eb659 100644 --- a/plugins/debug/debugPanel.js +++ b/plugins/debug/debugPanel.js @@ -204,13 +204,14 @@ // draw the sprite rectangle if (me.debug.renderHitBox) { - var x = -this.anchorPoint.x * this.width; - var y = -this.anchorPoint.y * this.height; + var bounds = this.getBounds(); + var x = - (this.anchorPoint.x * this.width) - ((bounds.width - this.width) / 2); + var y = - (this.anchorPoint.y * this.height) - ((bounds.height - this.height) / 2); renderer.save(); renderer.setColor("green"); renderer.translate(x, y); - renderer.drawShape(this.getBounds()); + renderer.drawShape(bounds); renderer.restore(); } }); From 37d696e475c7afc9d9dbd06a48eb5628553b9dbf Mon Sep 17 00:00:00 2001 From: obiot Date: Tue, 27 Sep 2016 09:28:47 +0200 Subject: [PATCH 129/177] [#818] fix a regression in the translate function regression introduced here : https://github.com/melonjs/melonJS/commit/6694b9d8771c1dc95d0d9007c0e201a1d5468a60 --- src/math/matrix2.js | 6 +++--- tests/spec/mat2d-spec.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/math/matrix2.js b/src/math/matrix2.js index aca7bfd727..5fdab58f69 100644 --- a/src/math/matrix2.js +++ b/src/math/matrix2.js @@ -263,9 +263,9 @@ translate : function (x, y) { var a = this.val; - a[6] += x; - a[7] += y; - + a[6] += a[0] * x + a[3] * y; + a[7] += a[1] * x + a[4] * y; + return this; }, diff --git a/tests/spec/mat2d-spec.js b/tests/spec/mat2d-spec.js index 43d1ed00d2..49035f5585 100644 --- a/tests/spec/mat2d-spec.js +++ b/tests/spec/mat2d-spec.js @@ -44,7 +44,7 @@ describe("me.Matrix2d", function () { it("should translate all values properly", function () { var matA = new me.Matrix2d(1, 2, 0, 3, 4, 0, 5, 6, 1); - var result = "me.Matrix2d(1, 2, 0, 3, 4, 0, 7, 9, 1)"; + var result = "me.Matrix2d(1, 2, 0, 3, 4, 0, 16, 22, 1)"; matA.translate(2, 3); From 4f33ec56c40fe1f919c17e76f8cad653bc1518cb Mon Sep 17 00:00:00 2001 From: obiot Date: Thu, 29 Sep 2016 16:31:46 +0200 Subject: [PATCH 130/177] add a matrix transpose and invert method also completed the test unit for the consctructor (when passing a 2x2 matrix) and the setTransform methods --- src/math/matrix2.js | 59 +++++++++++++++++++++++++++++++++++++++- tests/spec/mat2d-spec.js | 27 +++++++++++++++++- 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/src/math/matrix2.js b/src/math/matrix2.js index 5fdab58f69..1de449f831 100644 --- a/src/math/matrix2.js +++ b/src/math/matrix2.js @@ -147,6 +147,63 @@ return this; }, + /** + * Transpose the value of this matrix. + * @name transpose + * @memberOf me.Matrix2d + * @function + * @return {me.Matrix2d} Reference to this object for method chaining + */ + transpose : function () { + var tmp, a = this.val; + + tmp = a[1]; + a[1] = a[3]; + a[3] = tmp; + tmp = a[2]; + a[2] = a[6]; + a[6] = tmp; + tmp = a[5]; + a[5] = a[7]; + a[7] = tmp; + + return this; + }, + + /** + * invert this matrix, causing it to apply the opposite transformation. + * @name invert + * @memberOf me.Matrix2d + * @function + * @return {me.Matrix2d} Reference to this object for method chaining + */ + invert : function () { + var val = this.val; + + var a = val[ 0 ], b = val[ 1 ], c = val[ 2 ], + d = val[ 3 ], e = val[ 4 ], f = val[ 5 ], + g = val[ 6 ], h = val[ 7 ], i = val[ 8 ]; + + var ta = i * e - f * h, + td = f * g - i * d, + tg = h * d - e * g; + + var n = a * ta + b * td + c * tg; + + val[ 0 ] = ta / n; + val[ 1 ] = ( c * h - i * b ) / n; + val[ 2 ] = ( f * b - c * e ) / n; + + val[ 3 ] = td / n; + val[ 4 ] = ( i * a - c * g ) / n; + val[ 5 ] = ( c * d - f * a ) / n; + + val[ 6 ] = tg / n; + val[ 7 ] = ( b * g - h * a ) / n; + val[ 8 ] = ( e * a - b * d ) / n; + + return this; + }, /** * Transforms the given vector according to this matrix. @@ -265,7 +322,7 @@ a[6] += a[0] * x + a[3] * y; a[7] += a[1] * x + a[4] * y; - + return this; }, diff --git a/tests/spec/mat2d-spec.js b/tests/spec/mat2d-spec.js index 49035f5585..0e97cc67c3 100644 --- a/tests/spec/mat2d-spec.js +++ b/tests/spec/mat2d-spec.js @@ -6,6 +6,12 @@ describe("me.Matrix2d", function () { expect(matA.toString() === result).toEqual(true); }); + it("should be initialized properly with a 2x2 identity matrix", function () { + var matA = new me.Matrix2d(1, 0, 0, 1, 0, 0); + + expect(matA.isIdentity()).toEqual(true); + }); + it("should multiply all values properly", function () { var matA = new me.Matrix2d(1, 2, 0, 3, 4, 0, 5, 6, 1); var matB = new me.Matrix2d(7, 8, 0, 9, 10, 0, 11, 12, 1); @@ -34,7 +40,7 @@ describe("me.Matrix2d", function () { }); it("should scale all values properly", function () { - var matA = new me.Matrix2d(1, 2, 0, 3, 4, 0, 5, 6, 1); + var matA = new me.Matrix2d().setTransform(1, 2, 0, 3, 4, 0, 5, 6, 1); var result = "me.Matrix2d(2, 4, 0, 9, 12, 0, 5, 6, 1)"; matA.scale(2, 3); @@ -50,4 +56,23 @@ describe("me.Matrix2d", function () { expect(matA.toString() === result).toEqual(true); }); + + it("should transpose the matrix properly", function () { + var matA = new me.Matrix2d(1, 2, 3, 4, 5, 6, 7, 8, 9); + var result = "me.Matrix2d(1, 4, 7, 2, 5, 8, 3, 6, 9)"; + + matA.transpose(); + + expect(matA.toString() === result).toEqual(true); + }); + + it("should invert the matrix properly", function () { + var matA = new me.Matrix2d(4, 2, 3, 3, 1, 3, 2, -1, 4); + var result = "me.Matrix2d(7, -11, 3, -6, 10, -3, -5, 8, -2)"; + + matA.invert(); + + expect(matA.toString() === result).toEqual(true); + }); + }); From 422228fa3466905c42a21b3f6660d2dbc35e3531 Mon Sep 17 00:00:00 2001 From: obiot Date: Tue, 11 Oct 2016 14:19:14 +0800 Subject: [PATCH 131/177] subPixel rendering option false by default --- examples/platformer/js/game.js | 2 +- src/entity/entity.js | 8 ++-- src/game.js | 4 +- src/level/TMXLayer.js | 8 ++-- src/renderable/container.js | 4 +- src/renderable/sprite.js | 6 +-- src/renderable/viewport.js | 14 +++---- src/video/canvas/canvas_renderer.js | 57 ++++++++++++++++++++++++----- src/video/renderer.js | 2 + src/video/video.js | 1 + src/video/webgl/webgl_renderer.js | 19 +++++++++- 11 files changed, 92 insertions(+), 33 deletions(-) diff --git a/examples/platformer/js/game.js b/examples/platformer/js/game.js index 279642885b..41aa2e6ec8 100644 --- a/examples/platformer/js/game.js +++ b/examples/platformer/js/game.js @@ -18,7 +18,7 @@ var game = { onload: function() { // init the video - if (!me.video.init(800, 600, {wrapper : "screen", scale : "auto", scaleMethod : "flex-width" })) { + if (!me.video.init(800, 600, {wrapper : "screen", scale : "auto", scaleMethod : "flex-width", subPixel : false })) { alert("Your browser does not support HTML5 canvas."); return; } diff --git a/src/entity/entity.js b/src/entity/entity.js index c90dba0ba2..0c6fffe1f4 100644 --- a/src/entity/entity.js +++ b/src/entity/entity.js @@ -284,8 +284,8 @@ var ax = this.anchorPoint.x * this.body.width, ay = this.anchorPoint.y * this.body.height; - var x = ~~(0.5 + this.pos.x + this.body.pos.x + ax), - y = ~~(0.5 + this.pos.y + this.body.pos.y + ay); + var x = this.pos.x + this.body.pos.x + ax, + y = this.pos.y + this.body.pos.y + ay; renderer.translate(x, y); @@ -293,8 +293,8 @@ if (child.autoTransform === true && !child.currentTransform.isIdentity()) { // calculate the anchor point var bounds = child.getBounds(); - var cx = ~~(0.5 + (bounds.width * child.anchorPoint.x)); - var cy = ~~(0.5 + (bounds.height * child.anchorPoint.y)); + var cx = bounds.width * child.anchorPoint.x; + var cy = bounds.height * child.anchorPoint.y; renderer.save(); diff --git a/src/game.js b/src/game.js index ddab92b93d..171a8887f3 100644 --- a/src/game.js +++ b/src/game.js @@ -326,8 +326,8 @@ if (isDirty || isAlwaysDirty) { // cache the viewport rendering position, so that other object // can access it later (e,g. entityContainer when drawing floating objects) - var translateX = api.viewport.pos.x + ~~api.viewport.offset.x; - var translateY = api.viewport.pos.y + ~~api.viewport.offset.y; + var translateX = api.viewport.pos.x + api.viewport.offset.x; + var translateY = api.viewport.pos.y + api.viewport.offset.y; // translate the world coordinates by default to screen coordinates api.world.currentTransform.translate(-translateX, -translateY); diff --git a/src/level/TMXLayer.js b/src/level/TMXLayer.js index 6f2f695da3..b5580be058 100644 --- a/src/level/TMXLayer.js +++ b/src/level/TMXLayer.js @@ -277,8 +277,8 @@ * See https://github.com/melonjs/melonJS/issues/741#issuecomment-138431532 * for a thorough description of how this works. */ - x = ~~(ax * (rx - 1) * (bw - viewport.width) + this.offset.x - rx * vpos.x), - y = ~~(ay * (ry - 1) * (bh - viewport.height) + this.offset.y - ry * vpos.y); + x = ax * (rx - 1) * (bw - viewport.width) + this.offset.x - rx * vpos.x, + y = ay * (ry - 1) * (bh - viewport.height) + this.offset.y - ry * vpos.y; // Repeat horizontally; start drawing from left boundary @@ -315,8 +315,8 @@ alpha = renderer.globalAlpha(); if (this.ratio.x === this.ratio.y === 0) { - x = ~~(x + ax * (bw - width)); - y = ~~(y + ay * (bh - height)); + x = x + ax * (bw - width); + y = y + ay * (bh - height); } renderer.setGlobalAlpha(alpha * this.getOpacity()); diff --git a/src/renderable/container.js b/src/renderable/container.js index bc490a2944..fa95d701e5 100644 --- a/src/renderable/container.js +++ b/src/renderable/container.js @@ -789,8 +789,8 @@ // calculate the anchor point var bounds = obj.getBounds(); var anchor = obj.anchorPoint; - x = ~~(0.5 + (bounds.width * anchor.x )); - y = ~~(0.5 + (bounds.height * anchor.y )); + x = bounds.width * anchor.x; + y = bounds.height * anchor.y; if (hasTransform) { renderer.save(); diff --git a/src/renderable/sprite.js b/src/renderable/sprite.js index 58f0cb88db..ca95486a1f 100644 --- a/src/renderable/sprite.js +++ b/src/renderable/sprite.js @@ -521,9 +521,9 @@ // the frame to draw var frame = this.current; - // clamp position vector to pixel grid - var xpos = ~~this.pos.x, - ypos = ~~this.pos.y; + // cache the current position and size + var xpos = this.pos.x, + ypos = this.pos.y; var w = frame.width, h = frame.height; diff --git a/src/renderable/viewport.js b/src/renderable/viewport.js index 0ca2214b11..157f559af1 100644 --- a/src/renderable/viewport.js +++ b/src/renderable/viewport.js @@ -93,10 +93,10 @@ _followH : function (target) { var _x = this.pos.x; if ((target.x - this.pos.x) > (this.deadzone.right)) { - this.pos.x = ~~MIN((target.x) - (this.deadzone.right), this.bounds.width - this.width); + this.pos.x = MIN((target.x) - (this.deadzone.right), this.bounds.width - this.width); } else if ((target.x - this.pos.x) < (this.deadzone.pos.x)) { - this.pos.x = ~~MAX((target.x) - this.deadzone.pos.x, this.bounds.pos.x); + this.pos.x = MAX((target.x) - this.deadzone.pos.x, this.bounds.pos.x); } return (_x !== this.pos.x); }, @@ -105,10 +105,10 @@ _followV : function (target) { var _y = this.pos.y; if ((target.y - this.pos.y) > (this.deadzone.bottom)) { - this.pos.y = ~~MIN((target.y) - (this.deadzone.bottom), this.bounds.height - this.height); + this.pos.y = MIN((target.y) - (this.deadzone.bottom), this.bounds.height - this.height); } else if ((target.y - this.pos.y) < (this.deadzone.pos.y)) { - this.pos.y = ~~MAX((target.y) - this.deadzone.pos.y, this.bounds.pos.y); + this.pos.y = MAX((target.y) - this.deadzone.pos.y, this.bounds.pos.y); } return (_y !== this.pos.y); }, @@ -247,7 +247,7 @@ * me.game.viewport.move(0, -4); */ move : function (x, y) { - this.moveTo(~~(this.pos.x + x), ~~(this.pos.y + y)); + this.moveTo(this.pos.x + x, this.pos.y + y); }, /** @@ -261,11 +261,11 @@ */ moveTo : function (x, y) { - this.pos.x = (~~x).clamp( + this.pos.x = x.clamp( this.bounds.pos.x, this.bounds.width - this.width ); - this.pos.y = (~~y).clamp( + this.pos.y = y.clamp( this.bounds.pos.y, this.bounds.height - this.height ); diff --git a/src/video/canvas/canvas_renderer.js b/src/video/canvas/canvas_renderer.js index f59e35af40..7257bf13f0 100644 --- a/src/video/canvas/canvas_renderer.js +++ b/src/video/canvas/canvas_renderer.js @@ -20,6 +20,7 @@ * @param {Boolean} [options.doubleBuffering=false] Whether to enable double buffering * @param {Boolean} [options.antiAlias=false] Whether to enable anti-aliasing * @param {Boolean} [options.transparent=false] Whether to enable transparency on the canvas (performance hit when enabled) + * @param {Boolean} [options.subPixel=false] Whether to enable subpixel renderering (performance hit when enabled) * @param {Boolean} [options.textureSeamFix=true] enable the texture seam fix when rendering Tile when antiAlias is off for the canvasRenderer * @param {Number} [options.zoomX=width] The actual width of the canvas with scaling applied * @param {Number} [options.zoomY=height] The actual height of the canvas with scaling applied @@ -172,12 +173,39 @@ * renderer.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh); * // dx, dy, dw, dh being the destination target & dimensions. sx, sy, sw, sh being the position & dimensions to take from the image */ - drawImage : function () { + drawImage : function (image, sx, sy, sw, sh, dx, dy, dw, dh) { if (this.backBufferContext2D.globalAlpha < 1 / 255) { // Fast path: don't draw fully transparent return; } - this.backBufferContext2D.drawImage.apply(this.backBufferContext2D, arguments); + + if (typeof sw === "undefined") { + sw = dw = image.width; + sh = dh = image.height; + dx = sx; + dy = sy; + sx = 0; + sy = 0; + } + else if (typeof dx === "undefined") { + dx = sx; + dy = sy; + dw = sw; + dh = sh; + sw = image.width; + sh = image.height; + sx = 0; + sy = 0; + } + + if (this.subPixel === false) { + // clamp to pixel grid + dx = ~~dx; + dy = ~~dy; + } + + this.backBufferContext2D.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh); + //this.backBufferContext2D.drawImage.apply(this.backBufferContext2D, arguments); }, /** @@ -222,7 +250,7 @@ } this.backBufferContext2D.save(); this.backBufferContext2D.beginPath(); - this.backBufferContext2D.translate(x + radius, y + radius); + this.translate(x + radius, y + radius); this.backBufferContext2D.arc(0, 0, radius, start, end, antiClockwise || false); this.backBufferContext2D.fill(); this.backBufferContext2D.closePath(); @@ -393,7 +421,7 @@ } this.save(); this.backBufferContext2D.beginPath(); - this.backBufferContext2D.translate(x + radius, y + radius); + this.translate(x + radius, y + radius); this.backBufferContext2D.arc(0, 0, radius, start, end, antiClockwise || false); this.backBufferContext2D.stroke(); this.backBufferContext2D.closePath(); @@ -476,7 +504,7 @@ return; } this.save(); - this.backBufferContext2D.translate(poly.pos.x, poly.pos.y); + this.translate(poly.pos.x, poly.pos.y); this.backBufferContext2D.beginPath(); this.backBufferContext2D.moveTo(poly.points[0].x, poly.points[0].y); var point; @@ -487,7 +515,6 @@ this.backBufferContext2D.lineTo(poly.points[0].x, poly.points[0].y); this.backBufferContext2D.stroke(); this.backBufferContext2D.closePath(); - this.backBufferContext2D.translate(-poly.pos.x, -poly.pos.y); this.restore(); }, @@ -565,13 +592,21 @@ */ transform : function (mat2d) { var a = mat2d.val; + var tx = a[6], + ty = a[7]; + + if (this.subPixel === false) { + tx = ~~tx; + ty = ~~ty; + } + this.backBufferContext2D.transform( a[0], a[1], a[3], a[4], - a[6], - a[7] + tx, + ty ); }, @@ -584,7 +619,11 @@ * @param {Number} y */ translate : function (x, y) { - this.backBufferContext2D.translate(x, y); + if (this.subPixel === false) { + this.backBufferContext2D.translate(~~x, ~~y); + } else { + this.backBufferContext2D.translate(x, y); + } } }); diff --git a/src/video/renderer.js b/src/video/renderer.js index 1540cacb39..603bb07dcb 100644 --- a/src/video/renderer.js +++ b/src/video/renderer.js @@ -20,6 +20,7 @@ * @param {Boolean} [options.doubleBuffering=false] Whether to enable double buffering * @param {Boolean} [options.antiAlias=false] Whether to enable anti-aliasing * @param {Boolean} [options.transparent=false] Whether to enable transparency on the canvas (performance hit when enabled) + * @param {Boolean} [options.subPixel=false] Whether to enable subpixel rendering (performance hit when enabled) * @param {Number} [options.zoomX=width] The actual width of the canvas with scaling applied * @param {Number} [options.zoomY=height] The actual height of the canvas with scaling applied */ @@ -36,6 +37,7 @@ this.transparent = !!(options.transparent); this.doubleBuffering = !!(options.doubleBuffering); this.antiAlias = !!(options.antiAlias); + this.subPixel = !!(options.subPixel); this.gameWidthZoom = options.zoomX || width; this.gameHeightZoom = options.zoomY || height; diff --git a/src/video/video.js b/src/video/video.js index f2e516ae5d..38c1dfd16e 100644 --- a/src/video/video.js +++ b/src/video/video.js @@ -38,6 +38,7 @@ scaleMethod : "fit", transparent : false, antiAlias : false, + subPixel : false }; diff --git a/src/video/webgl/webgl_renderer.js b/src/video/webgl/webgl_renderer.js index 3e7a8c5b03..f313bb9e62 100644 --- a/src/video/webgl/webgl_renderer.js +++ b/src/video/webgl/webgl_renderer.js @@ -19,6 +19,7 @@ * @param {Boolean} [options.doubleBuffering=false] Whether to enable double buffering * @param {Boolean} [options.antiAlias=false] Whether to enable anti-aliasing * @param {Boolean} [options.transparent=false] Whether to enable transparency on the canvas (performance hit when enabled) + * @param {Boolean} [options.subPixel=false] Whether to enable subpixel renderering (performance hit when enabled) * @param {Number} [options.zoomX=width] The actual width of the canvas with scaling applied * @param {Number} [options.zoomY=height] The actual height of the canvas with scaling applied * @param {me.WebGLRenderer.Compositor} [options.compositor] A class that implements the compositor API @@ -321,6 +322,12 @@ sy = 0; } + if (this.subPixel === false) { + // clamp to pixel grid + dx = ~~dx; + dy = ~~dy; + } + var key = sx + "," + sy + "," + sw + "," + sh; this.compositor.addQuad(this.cache.get(image), key, dx, dy, dw, dh); }, @@ -705,6 +712,12 @@ */ transform : function (mat2d) { this.currentTransform.multiply(mat2d); + if (this.subPixel === false) { + // snap position values to pixel grid + var a = this.currentTransform.val; + a[6] = ~~a[6]; + a[7] = ~~a[7]; + } }, /** @@ -716,7 +729,11 @@ * @param {Number} y */ translate : function (x, y) { - this.currentTransform.translate(x, y); + if (this.subPixel === false) { + this.currentTransform.translate(~~x, ~~y); + } else { + this.currentTransform.translate(x, y); + } } }); From f1330bf425fa59b3b46cb9af7bccdda7f1630342 Mon Sep 17 00:00:00 2001 From: obiot Date: Wed, 12 Oct 2016 14:23:26 +0800 Subject: [PATCH 132/177] some code improvements when `subPixel` is on --- src/video/canvas/canvas_renderer.js | 46 +++++++++++++---------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/src/video/canvas/canvas_renderer.js b/src/video/canvas/canvas_renderer.js index 7257bf13f0..f6a806d26c 100644 --- a/src/video/canvas/canvas_renderer.js +++ b/src/video/canvas/canvas_renderer.js @@ -179,33 +179,29 @@ return; } - if (typeof sw === "undefined") { - sw = dw = image.width; - sh = dh = image.height; - dx = sx; - dy = sy; - sx = 0; - sy = 0; - } - else if (typeof dx === "undefined") { - dx = sx; - dy = sy; - dw = sw; - dh = sh; - sw = image.width; - sh = image.height; - sx = 0; - sy = 0; - } - if (this.subPixel === false) { - // clamp to pixel grid - dx = ~~dx; - dy = ~~dy; + if (typeof sw === "undefined") { + sw = dw = image.width; + sh = dh = image.height; + dx = sx; + dy = sy; + sx = 0; + sy = 0; + } + else if (typeof dx === "undefined") { + dx = sx; + dy = sy; + dw = sw; + dh = sh; + sw = image.width; + sh = image.height; + sx = 0; + sy = 0; + } + this.backBufferContext2D.drawImage(image, sx, sy, sw, sh, ~~dx, ~~dy, dw, dh); + } else { + this.backBufferContext2D.drawImage.apply(this.backBufferContext2D, arguments); } - - this.backBufferContext2D.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh); - //this.backBufferContext2D.drawImage.apply(this.backBufferContext2D, arguments); }, /** From 6cef45210e36bc09d5463fbfb63db0ec146bfc29 Mon Sep 17 00:00:00 2001 From: obiot Date: Wed, 12 Oct 2016 14:38:43 +0800 Subject: [PATCH 133/177] `beginPath` is not part of the drawing state, so calling save and restore here serve no purpose that's a loooooooots of context saving & restoring avoided once in debug mode ! --- src/video/canvas/canvas_renderer.js | 23 ++++++++++------------- src/video/webgl/webgl_renderer.js | 4 ---- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/video/canvas/canvas_renderer.js b/src/video/canvas/canvas_renderer.js index f6a806d26c..7daf89b593 100644 --- a/src/video/canvas/canvas_renderer.js +++ b/src/video/canvas/canvas_renderer.js @@ -244,13 +244,13 @@ // Fast path: don't draw fully transparent return; } - this.backBufferContext2D.save(); - this.backBufferContext2D.beginPath(); + this.translate(x + radius, y + radius); + this.backBufferContext2D.beginPath(); this.backBufferContext2D.arc(0, 0, radius, start, end, antiClockwise || false); this.backBufferContext2D.fill(); this.backBufferContext2D.closePath(); - this.backBufferContext2D.restore(); + this.translate(- (x + radius), -(y + radius)); }, /** @@ -415,13 +415,12 @@ // Fast path: don't draw fully transparent return; } - this.save(); - this.backBufferContext2D.beginPath(); this.translate(x + radius, y + radius); + this.backBufferContext2D.beginPath(); this.backBufferContext2D.arc(0, 0, radius, start, end, antiClockwise || false); this.backBufferContext2D.stroke(); this.backBufferContext2D.closePath(); - this.restore(); + this.translate(-(x + radius), -(y + radius)); }, /** @@ -439,8 +438,7 @@ // Fast path: don't draw fully transparent return; } - this.save(); - this.context.beginPath(); + var hw = w, hh = h, lx = x - hw, @@ -455,13 +453,13 @@ ymin = y - ymagic, ymax = y + ymagic; + this.backBufferContext2D.beginPath(); this.backBufferContext2D.moveTo(x, ty); this.backBufferContext2D.bezierCurveTo(xmax, ty, rx, ymin, rx, y); this.backBufferContext2D.bezierCurveTo(rx, ymax, xmax, by, x, by); this.backBufferContext2D.bezierCurveTo(xmin, by, lx, ymax, lx, y); this.backBufferContext2D.bezierCurveTo(lx, ymin, xmin, ty, x, ty); this.backBufferContext2D.stroke(); - this.restore(); }, /** @@ -479,12 +477,11 @@ // Fast path: don't draw fully transparent return; } - this.save(); + this.backBufferContext2D.beginPath(); this.backBufferContext2D.moveTo(startX, startY); this.backBufferContext2D.lineTo(endX, endY); this.backBufferContext2D.stroke(); - this.restore(); }, /** @@ -499,7 +496,7 @@ // Fast path: don't draw fully transparent return; } - this.save(); + this.translate(poly.pos.x, poly.pos.y); this.backBufferContext2D.beginPath(); this.backBufferContext2D.moveTo(poly.points[0].x, poly.points[0].y); @@ -511,7 +508,7 @@ this.backBufferContext2D.lineTo(poly.points[0].x, poly.points[0].y); this.backBufferContext2D.stroke(); this.backBufferContext2D.closePath(); - this.restore(); + this.translate(-poly.pos.x, -poly.pos.y); }, /** diff --git a/src/video/webgl/webgl_renderer.js b/src/video/webgl/webgl_renderer.js index f313bb9e62..155a4fdd38 100644 --- a/src/video/webgl/webgl_renderer.js +++ b/src/video/webgl/webgl_renderer.js @@ -663,11 +663,8 @@ if (shape.shapeType === "Rectangle") { this.strokeRect(shape.left, shape.top, shape.width, shape.height); } else if (shape instanceof me.Line || shape instanceof me.Polygon) { - this.save(); this.strokePolygon(shape); - this.restore(); } else if (shape instanceof me.Ellipse) { - this.save(); if (shape.radiusV.x === shape.radiusV.y) { // it's a circle this.strokeArc( @@ -686,7 +683,6 @@ shape.radiusV.y ); } - this.restore(); } }, From d42dba4f903a61f475a63aec403008dfb78c87ac Mon Sep 17 00:00:00 2001 From: obiot Date: Wed, 12 Oct 2016 14:44:31 +0800 Subject: [PATCH 134/177] fix sub-pixel font drawing --- src/font/bitmapfont.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/font/bitmapfont.js b/src/font/bitmapfont.js index f0dc27f231..a918f31fb3 100644 --- a/src/font/bitmapfont.js +++ b/src/font/bitmapfont.js @@ -201,8 +201,8 @@ renderer.drawImage(this.fontImage, glyph.src.x, glyph.src.y, glyph.width, glyph.height, - ~~(x + glyph.offset.x), - ~~(y + glyph.offset.y * this.fontScale.y), + x + glyph.offset.x, + y + glyph.offset.y * this.fontScale.y, glyph.width * this.fontScale.x, glyph.height * this.fontScale.y); x += (glyph.xadvance + kerning) * this.fontScale.x; lastGlyph = glyph; From 1c13875d1e138d9f3596374e2075f4c08a39959a Mon Sep 17 00:00:00 2001 From: obiot Date: Wed, 12 Oct 2016 15:04:47 +0800 Subject: [PATCH 135/177] CHANGELOG update --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 13755787bf..34e0c29ad8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,7 @@ Version History * Font : added true support for bitmap font generated out of true type fonts (e.g. Littera, bmfont, bmglypgh, etc...) * Renderable : transformations are now directly defined under me.Renderable and can be automatically applied through the `autoTransform` flag * Renderer : simplified/aligned both the Canvas and WebGL renderer API in a more WebGL fashion +* Renderer : added a subPixel rendering option (off by default) * Pointer : fixed bug with Chrome on Windows touch devices where touch input would register, but mouse input would not. (credit to BrianRosamilia) * TMX : scaling and rotation can now be applied to a tilemap (see me.levelDirector.loadLevel() for example) From 74dae69e385b38744d7797bbdc485e2a2e1f8798 Mon Sep 17 00:00:00 2001 From: obiot Date: Wed, 12 Oct 2016 16:40:04 +0800 Subject: [PATCH 136/177] also fix subpixel rendering for particles --- src/particles/particle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/particles/particle.js b/src/particles/particle.js index 8dcec4531e..9948a9f4de 100644 --- a/src/particles/particle.js +++ b/src/particles/particle.js @@ -129,7 +129,7 @@ this.currentTransform.setTransform( scale, 0, 0, 0, scale, 0, - ~~this.pos.x, ~~this.pos.y, 1 + this.pos.x, this.pos.y, 1 ).rotate(angle); // Return true if the particle is not dead yet From f6fab6fd8d72aee8693f956085edf7a4d4c43568 Mon Sep 17 00:00:00 2001 From: obiot Date: Fri, 14 Oct 2016 12:37:02 +0800 Subject: [PATCH 137/177] [#820] clarify documentation on the renderable component --- src/entity/entity.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/entity/entity.js b/src/entity/entity.js index 0c6fffe1f4..81b1abadf6 100644 --- a/src/entity/entity.js +++ b/src/entity/entity.js @@ -35,7 +35,7 @@ init : function (x, y, settings) { /** - * The entity renderable object (if defined) + * The entity renderable component (can be any objects deriving from me.Renderable, like me.Sprite for example) * @public * @type me.Renderable * @name renderable From ae7e10642d9c86fecc5dccdd4fdc554e0977fb8b Mon Sep 17 00:00:00 2001 From: obiot Date: Mon, 17 Oct 2016 13:22:24 +0800 Subject: [PATCH 138/177] Howler 2.0.1 --- CHANGELOG | 5 ++--- src/audio/audio.js | 2 +- src/vendors/howler.core.js | 40 ++++++++++++++++++++++++++------------ 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 34e0c29ad8..3fef5f924f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,7 +2,7 @@ Version History --------------- 4.0.0 -* Audio : updated to Howler final 2.0.0 version +* Audio : updated to the latest Howler 2.0.1 version * Font : added true support for bitmap font generated out of true type fonts (e.g. Littera, bmfont, bmglypgh, etc...) * Renderable : transformations are now directly defined under me.Renderable and can be automatically applied through the `autoTransform` flag * Renderer : simplified/aligned both the Canvas and WebGL renderer API in a more WebGL fashion @@ -39,8 +39,7 @@ Version History * Core : fixed/improved compatibility with Ejecta (e.g. initialization, CAFF support) * Input : actually really truly fix pointer event z-ordering, finally -3.0.0 -* Audio : updated to the latest Howler v2.0.0 beta version +3.0.0* Audio : updated to the latest Howler v2.0.0 beta version * Audio : added a `me.audio.resume` function for added convenience (was already possible by calling back `me.audio.play`) * Container : added an `autoDepth` feature that auto-increments a child's z-coordinate when inserted * Color : add support for the #ARGB and #AARRGGBB formats diff --git a/src/audio/audio.js b/src/audio/audio.js index 79a5b1979e..a0542a6707 100644 --- a/src/audio/audio.js +++ b/src/audio/audio.js @@ -83,7 +83,7 @@ * configure and initialize the audio engine
* melonJS will try to load audio files corresponding to the browser supported audio format(s)
* below is the list of supported file extentions :
- * "mp3", "mpeg", opus", "ogg", "oga", "wav", "aac", "caf", "m4a", "mp4", "weba", "webm", "dolby"
+ * "mp3", "mpeg", opus", "ogg", "oga", "wav", "aac", "caf", "m4a", "mp4", "weba", "webm", "dolby", "flac"
* keep in mind that not all browsers can play all audio formats, and if no compatible codecs are detected, audio will be disabled. * @name init * @memberOf me.audio diff --git a/src/vendors/howler.core.js b/src/vendors/howler.core.js index e7e06d09f9..1267a60ac9 100644 --- a/src/vendors/howler.core.js +++ b/src/vendors/howler.core.js @@ -1,5 +1,5 @@ /*! - * howler.js v2.0.0 + * howler.js v2.0.1 * howlerjs.com * * (c) 2013-2016, James Simpson of GoldFire Studios @@ -176,7 +176,7 @@ * @return {Boolean} */ codecs: function(ext) { - return (this || Howler)._codecs[ext]; + return (this || Howler)._codecs[ext.replace(/^x-/, '')]; }, /** @@ -231,7 +231,8 @@ mp4: !!(audioTest.canPlayType('audio/x-mp4;') || audioTest.canPlayType('audio/mp4;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''), weba: !!audioTest.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, ''), webm: !!audioTest.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, ''), - dolby: !!audioTest.canPlayType('audio/mp4; codecs="ec-3"').replace(/^no$/, '') + dolby: !!audioTest.canPlayType('audio/mp4; codecs="ec-3"').replace(/^no$/, ''), + flac: !!(audioTest.canPlayType('audio/x-flac;') || audioTest.canPlayType('audio/flac;')).replace(/^no$/, '') }; return self; @@ -826,7 +827,7 @@ // Get the sound. var sound = self._soundById(ids[i]); - if (sound && !sound._paused) { + if (sound) { // Reset the seek position. sound._seek = sound._start || 0; sound._rateSeek = 0; @@ -840,6 +841,10 @@ if (self._webAudio) { // make sure the sound has been created if (!sound._node.bufferSource) { + if (!internal) { + self._emit('stop', sound._id); + } + return self; } @@ -936,7 +941,7 @@ if (args.length === 0) { // Return the value of the groups' volume. return self._volume; - } else if (args.length === 1) { + } else if (args.length === 1 || args.length === 2 && typeof args[1] === 'undefined') { // First check if this is an ID, and if not, assume it is a new volume. var ids = self._getSoundIds(); var index = ids.indexOf(args[0]); @@ -1014,7 +1019,13 @@ var diff = Math.abs(from - to); var dir = from > to ? 'out' : 'in'; var steps = diff / 0.01; - var stepLen = len / steps; + var stepLen = (steps > 0) ? len / steps : len; + + // Since browsers clamp timeouts to 4ms, we need to clamp our steps to that too. + if (stepLen < 4) { + steps = Math.ceil(steps / (4 / stepLen)); + stepLen = 4; + } // If the sound hasn't loaded, add it to the load queue to fade when capable. if (self._state !== 'loaded') { @@ -1055,8 +1066,10 @@ var vol = from; sound._interval = setInterval(function(soundId, sound) { - // Update the volume amount. - vol += (dir === 'in' ? 0.01 : -0.01); + // Update the volume amount, but only if the volume should change. + if (steps > 0) { + vol += (dir === 'in' ? 0.01 : -0.01); + } // Make sure the volume is in the right bounds. vol = Math.max(0, vol); @@ -1153,6 +1166,10 @@ sound._loop = loop; if (self._webAudio && sound._node && sound._node.bufferSource) { sound._node.bufferSource.loop = loop; + if (loop) { + sound._node.bufferSource.loopStart = sound._start || 0; + sound._node.bufferSource.loopEnd = sound._stop; + } } } } @@ -1445,6 +1462,9 @@ delete cache[self._src]; } + // Clear global errors. + Howler.noAudio = false; + // Clear out `self`. self._state = 'unloaded'; self._sounds = []; @@ -1906,10 +1926,6 @@ _errorListener: function() { var self = this; - if (self._node.error && self._node.error.code === 4) { - Howler.noAudio = true; - } - // Fire an error event and pass back the code. self._parent._emit('loaderror', self._id, self._node.error ? self._node.error.code : 0); From bca659885eb2782d3730193843b94da2b58d6e88 Mon Sep 17 00:00:00 2001 From: obiot Date: Mon, 17 Oct 2016 13:22:48 +0800 Subject: [PATCH 139/177] fix audio fx when touching an enemy --- examples/platformer/js/entities/player.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/platformer/js/entities/player.js b/examples/platformer/js/entities/player.js index 5981ee645e..e9234e4256 100644 --- a/examples/platformer/js/entities/player.js +++ b/examples/platformer/js/entities/player.js @@ -191,7 +191,7 @@ game.PlayerEntity = me.Entity.extend({ * ouch */ hurt : function () { - if (!this.renderable.flickering) + if (!this.renderable.isFlickering()) { this.renderable.flicker(750); // flash the screen From f2ad18568cd4dad75265f0cd2b54344251e335c1 Mon Sep 17 00:00:00 2001 From: obiot Date: Tue, 18 Oct 2016 10:23:09 +0700 Subject: [PATCH 140/177] [#642] add tests for renderables absolute position --- tests/spec/renderable-spec.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/spec/renderable-spec.js b/tests/spec/renderable-spec.js index 76a177b39c..094e8ea32b 100644 --- a/tests/spec/renderable-spec.js +++ b/tests/spec/renderable-spec.js @@ -25,4 +25,34 @@ describe("me.Renderable", function () { expect(renderable.getBounds().height).toEqual(20); }); }); + + describe("getAbsoluteBounds returns the correct value", function () { + var rootContainer = new me.Container(0, 0, 1000, 1000); + var childContainer = new me.Container(100, 100, 500, 500); + var renderable = new me.Renderable(50, 50, 50, 50); + + it("create and add a child container to the root container", function () { + rootContainer._root = true; + rootContainer.addChild(childContainer); + expect(childContainer.isAttachedToRoot()).toEqual(true); + }); + + it("renderable should have a correct absolute position once added", function () { + childContainer.addChild(renderable); + expect(renderable.getBounds().pos.x).toEqual(150); + expect(renderable.getBounds().pos.y).toEqual(150); + }); + + it("changing the renderable position, change the absolute pos", function () { + renderable.pos.set(200, 100, 0); + expect(renderable.getBounds().pos.x).toEqual(300); + expect(renderable.getBounds().pos.y).toEqual(200); + }); + + it("changing the parent container position, also change the renderable absolute pos", function () { + childContainer.pos.set(200, 200, 0); + expect(renderable.getBounds().pos.x).toEqual(400); + expect(renderable.getBounds().pos.y).toEqual(300); + }); + }); }); From bafb845f790cca74f26ef085c99c9c72d930bba1 Mon Sep 17 00:00:00 2001 From: obiot Date: Mon, 24 Oct 2016 10:32:46 +0800 Subject: [PATCH 141/177] CHANGELOG update --- CHANGELOG | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 3fef5f924f..7382a401e5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,14 +1,24 @@ Version History --------------- 4.0.0 - * Audio : updated to the latest Howler 2.0.1 version +* Core : largely improved and completed the matrix implementation and usage +* Core : Object.assign polyfill update +* Core : various improvements and fixes to jasmine test units. * Font : added true support for bitmap font generated out of true type fonts (e.g. Littera, bmfont, bmglypgh, etc...) -* Renderable : transformations are now directly defined under me.Renderable and can be automatically applied through the `autoTransform` flag +* Particles : reduced creation/use of temporary (vector) objects +* Pointer : fixed bug with Chrome on Windows touch devices where touch input would register, but mouse input would not. (credit to BrianRosamilia) +* Pointer : PointerEvent detection is now fully W3C compliant +* Pointer : automaticaly free any defined region if there is no callback left +* Renderable : added a new `currentTransform` property that defines the renderable transformation matrix +* Renderable : transformations can now be automatically applied through the `autoTransform` flag (off by default for user-defined objects) +* Renderable : me.AnimationSheet has been merged into me.Sprite (me.AnimationSheet is now only an alias), and rendering function simplified +* Renderable : fixed image layer first update, when added to a child container (and not directly the root one) * Renderer : simplified/aligned both the Canvas and WebGL renderer API in a more WebGL fashion * Renderer : added a subPixel rendering option (off by default) -* Pointer : fixed bug with Chrome on Windows touch devices where touch input would register, but mouse input would not. (credit to BrianRosamilia) +* Shapes : polygon based shapes can now be freely transformed, and bounding box will be resized accordingly * TMX : scaling and rotation can now be applied to a tilemap (see me.levelDirector.loadLevel() for example) +* TMX : fixed some issues when loading TMX level into a child container. 3.1.0 * Audio : exposed a `me.audio.rate` function to allow modifying the playback rate of the specified audio clip. @@ -39,7 +49,8 @@ Version History * Core : fixed/improved compatibility with Ejecta (e.g. initialization, CAFF support) * Input : actually really truly fix pointer event z-ordering, finally -3.0.0* Audio : updated to the latest Howler v2.0.0 beta version +3.0.0 +* Audio : updated to the latest Howler v2.0.0 beta version * Audio : added a `me.audio.resume` function for added convenience (was already possible by calling back `me.audio.play`) * Container : added an `autoDepth` feature that auto-increments a child's z-coordinate when inserted * Color : add support for the #ARGB and #AARRGGBB formats From a0e084ad68c58f6a9a0599982c6d4b39f02176f7 Mon Sep 17 00:00:00 2001 From: obiot Date: Mon, 24 Oct 2016 12:36:28 +0800 Subject: [PATCH 142/177] fixed a regression with the `isCurrentAnimation` function somehow the `name` property of the current animation is being replaced by `0` ! this needs a proper fix, but to do that, generally how data is being stored in me.Sprite needs some rethinking/cleaning --- src/renderable/sprite.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/renderable/sprite.js b/src/renderable/sprite.js index ca95486a1f..fc455c503c 100644 --- a/src/renderable/sprite.js +++ b/src/renderable/sprite.js @@ -375,6 +375,8 @@ this.current = this.anim[name]; this.resetAnim = resetAnim || null; this.setAnimationFrame(this.current.idx); + // XXX this should not be overwritten + this.current.name = name; if (!_preserve_dt) { this.dt = 0; } From 25073ca94befcc845c21f1696cb26d0ae9971df5 Mon Sep 17 00:00:00 2001 From: obiot Date: Mon, 24 Oct 2016 12:41:07 +0800 Subject: [PATCH 143/177] [Jasmine --- tests/spec/sprite-spec.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/spec/sprite-spec.js b/tests/spec/sprite-spec.js index 4e2d4c2316..ad2c8ae348 100644 --- a/tests/spec/sprite-spec.js +++ b/tests/spec/sprite-spec.js @@ -21,6 +21,14 @@ describe("me.Sprite", function () { expect(sprite.addAnimation("test", [ 0, 1 ])).toEqual(2); }); + it("me.Sprite isCurrentAnimation allows to verify which animation is set", function () { + expect(sprite.addAnimation("reverse_test", [ 1, 0 ])).toEqual(2); + sprite.setCurrentAnimation("reverse_test"); + expect(sprite.isCurrentAnimation("reverse_test")).toEqual(true); + expect(sprite.isCurrentAnimation("test")).toEqual(false); + }); + + // TODO: add bounds testing when transformation are added }); From 92da9cd3789755dec3caa6a19f569cb85cc30fac Mon Sep 17 00:00:00 2001 From: obiot Date: Mon, 24 Oct 2016 12:41:18 +0800 Subject: [PATCH 144/177] Revert "[Jasmine" This reverts commit 25073ca94befcc845c21f1696cb26d0ae9971df5. --- tests/spec/sprite-spec.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/spec/sprite-spec.js b/tests/spec/sprite-spec.js index ad2c8ae348..4e2d4c2316 100644 --- a/tests/spec/sprite-spec.js +++ b/tests/spec/sprite-spec.js @@ -21,14 +21,6 @@ describe("me.Sprite", function () { expect(sprite.addAnimation("test", [ 0, 1 ])).toEqual(2); }); - it("me.Sprite isCurrentAnimation allows to verify which animation is set", function () { - expect(sprite.addAnimation("reverse_test", [ 1, 0 ])).toEqual(2); - sprite.setCurrentAnimation("reverse_test"); - expect(sprite.isCurrentAnimation("reverse_test")).toEqual(true); - expect(sprite.isCurrentAnimation("test")).toEqual(false); - }); - - // TODO: add bounds testing when transformation are added }); From 57b76616d97b205e5782501b9ba789e0148548d1 Mon Sep 17 00:00:00 2001 From: obiot Date: Mon, 24 Oct 2016 12:42:12 +0800 Subject: [PATCH 145/177] [Jasmine] add a test case for `isCurrentAnimation` (following the previous regression fix) --- tests/spec/sprite-spec.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/spec/sprite-spec.js b/tests/spec/sprite-spec.js index 4e2d4c2316..6d4fc942fd 100644 --- a/tests/spec/sprite-spec.js +++ b/tests/spec/sprite-spec.js @@ -21,6 +21,13 @@ describe("me.Sprite", function () { expect(sprite.addAnimation("test", [ 0, 1 ])).toEqual(2); }); + it("me.Sprite isCurrentAnimation allows to verify which animation is set", function () { + expect(sprite.addAnimation("reverse_test", [ 1, 0 ])).toEqual(2); + sprite.setCurrentAnimation("reverse_test"); + expect(sprite.isCurrentAnimation("reverse_test")).toEqual(true); + expect(sprite.isCurrentAnimation("test")).toEqual(false); + }); + // TODO: add bounds testing when transformation are added }); From 5e87dfd61e9fcf07eb139136bc24e627b38461ff Mon Sep 17 00:00:00 2001 From: obiot Date: Mon, 24 Oct 2016 15:52:23 +0800 Subject: [PATCH 146/177] align the renderable scale function signature with the matrix one also, highlight in the documentation that y is optional --- src/math/matrix2.js | 2 +- src/renderable/renderable.js | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/math/matrix2.js b/src/math/matrix2.js index 1de449f831..14a047057d 100644 --- a/src/math/matrix2.js +++ b/src/math/matrix2.js @@ -230,7 +230,7 @@ * @memberOf me.Matrix2d * @function * @param {Number} x a number representing the abscissa of the scaling vector. - * @param {Number} y a number representing the ordinate of the scaling vector. + * @param {Number} [y=x] a number representing the ordinate of the scaling vector. * @return {me.Matrix2d} Reference to this object for method chaining */ scale : function (x, y) { diff --git a/src/renderable/renderable.js b/src/renderable/renderable.js index 80b8ff7879..c98a435a35 100644 --- a/src/renderable/renderable.js +++ b/src/renderable/renderable.js @@ -283,14 +283,17 @@ * @memberOf me.Renderable * @function * @param {Number} x a number representing the abscissa of the scaling vector. - * @param {Number} y a number representing the ordinate of the scaling vector. + * @param {Number} [y=x] a number representing the ordinate of the scaling vector. * @return {me.Renderable} Reference to this object for method chaining */ scale : function (x, y) { + var _x = x, + _y = typeof(y) === "undefined" ? _x : y; + // set the scaleFlag - this.currentTransform.scale(x, y); + this.currentTransform.scale(_x, _y); // resize the bounding box - this.getBounds().resize(this.width * x, this.height * y); + this.getBounds().resize(this.width * _x, this.height * _y); return this; }, From f7eeec8dc1dd4d55414caef1ee93029ea8a65d0f Mon Sep 17 00:00:00 2001 From: obiot Date: Mon, 24 Oct 2016 16:07:51 +0800 Subject: [PATCH 147/177] added a test case for `me.Sprite.scale` to check for the corresponding resized bounding box --- tests/spec/sprite-spec.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/spec/sprite-spec.js b/tests/spec/sprite-spec.js index 6d4fc942fd..a6cbeeccd8 100644 --- a/tests/spec/sprite-spec.js +++ b/tests/spec/sprite-spec.js @@ -28,6 +28,21 @@ describe("me.Sprite", function () { expect(sprite.isCurrentAnimation("test")).toEqual(false); }); - // TODO: add bounds testing when transformation are added + it("me.Sprite bounds should be updated when the sprite is scaled", function () { + var bounds = sprite.getBounds(); + // scale the sprite + sprite.scale(2.0); // w & h -> 64, 64 + expect(bounds.width).toEqual(64); + expect(bounds.height).toEqual(64); + }); + /* TODO: add proper bounds testing when transformation are added + it("me.Sprite bounds should be updated when the sprite is scaled or rotated", function () { + var bounds = sprite.getBounds(); + // further scale the sprite (-> 128) and rotate it by 45deg + sprite.transform((new me.Matrix2d()).scale(2.0).rotate(45 * Math.PI/180)); + expect(Math.round(bounds.width)).toEqual(128 + (128 / 2)); + expect(Math.round(bounds.height)).toEqual(128 + (128 / 2)); + }); + */ }); From 295eefff559d17eea373f000e636b29758256c79 Mon Sep 17 00:00:00 2001 From: obiot Date: Tue, 25 Oct 2016 15:35:05 +0800 Subject: [PATCH 148/177] [#823] fix rendering of space characters with *packed* bitmapfont This issue occurs when "empty" char (like a space one) are packed and have a 0 width and height. melonJS was therefore trying to set a value for those, but this is not required since anyway these values are not used when rendering the string. --- src/font/bitmapfont.js | 5 ++++- src/font/bitmapfontdata.js | 23 ++--------------------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/src/font/bitmapfont.js b/src/font/bitmapfont.js index a918f31fb3..691ab7dfa0 100644 --- a/src/font/bitmapfont.js +++ b/src/font/bitmapfont.js @@ -203,7 +203,10 @@ glyph.width, glyph.height, x + glyph.offset.x, y + glyph.offset.y * this.fontScale.y, - glyph.width * this.fontScale.x, glyph.height * this.fontScale.y); + glyph.width * this.fontScale.x, glyph.height * this.fontScale.y + ); + + // increment position x += (glyph.xadvance + kerning) * this.fontScale.x; lastGlyph = glyph; } diff --git a/src/font/bitmapfontdata.js b/src/font/bitmapfontdata.js index fc4b3c5e10..f8620456d6 100644 --- a/src/font/bitmapfontdata.js +++ b/src/font/bitmapfontdata.js @@ -17,7 +17,6 @@ me.BitmapFontData = me.Object.extend({ this.capHeight = 1; // The distance from the bottom of the glyph that extends the lowest to the baseline. This number is negative. this.descent = 0; - this.down = 0; this.scale = new me.Vector2d(); /** @@ -28,17 +27,12 @@ me.BitmapFontData = me.Object.extend({ */ this.glyphs = {}; - /** - * The width of the space glyph - * @name spaceWidth - * @type {Number} - * @memberOf me.BitmapFontData - */ - this.spaceWidth = 0; this.xChars = ["x", "e", "a", "o", "n", "s", "r", "c", "u", "m", "v", "w", "z"]; this.capChars = ["M", "N", "B", "D", "C", "E", "F", "K", "A", "G", "H", "I", "J", "L", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]; + + // parse the data this.parse(data); }, @@ -58,17 +52,6 @@ me.BitmapFontData = me.Object.extend({ glyph.xadvance = this._getFirstGlyph().xadvance; this.glyphs[spaceCharCode] = glyph; } - - if (glyph.width === 0) { - glyph.width = ~~(this.padLeft + glyph.xadvance + this.padRight); - glyph.offset.set(-this.padLeft, 0); - } - - if (glyph.height === 0) { - glyph.height = this._getFirstGlyph().height; - } - - this.spaceWidth = glyph.width; }, /** @@ -212,7 +195,5 @@ me.BitmapFontData = me.Object.extend({ } this.capHeight -= padY; - - this.down = -this.lineHeight; } }); From f3e8010120ab7c650e911f9d0ae024ab04adea13 Mon Sep 17 00:00:00 2001 From: obiot Date: Wed, 26 Oct 2016 11:05:27 +0800 Subject: [PATCH 149/177] add a getter function for the global/current color (we already had a matching setColor) @agmcleod bad boy using private/undocumented property in the space invaders tutorial :) --- src/video/renderer.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/video/renderer.js b/src/video/renderer.js index 603bb07dcb..148c5a65aa 100644 --- a/src/video/renderer.js +++ b/src/video/renderer.js @@ -228,6 +228,17 @@ return this.backBufferCanvas.height; }, + /** + * get the current fill & stroke style color. + * @name getColor + * @memberOf me.Renderer + * @function + * @param {me.Color} current global color + */ + getColor : function () { + return this.currentColor; + }, + /** * return the current global alpha * @name globalAlpha From 867ee3d10b49217a081505335131849744df731d Mon Sep 17 00:00:00 2001 From: obiot Date: Thu, 27 Oct 2016 12:17:19 +0800 Subject: [PATCH 150/177] [jasmine] add a simple polygon transfromation test unit --- tests/spec/polygon-spec.js | 23 ++++++++++++++++++++--- tests/spec/sprite-spec.js | 10 ---------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/tests/spec/polygon-spec.js b/tests/spec/polygon-spec.js index dd2dbbbdd7..18a9e79530 100644 --- a/tests/spec/polygon-spec.js +++ b/tests/spec/polygon-spec.js @@ -15,9 +15,6 @@ describe("Shape : me.Polygon", function () { {x: -28, y: 60} ]); - // get the polygon bounding rect - var boundingRect = stars.getBounds(); - describe("Polygon", function () { it("requires at least 3 points", function () { function badPolygon() { @@ -40,6 +37,9 @@ describe("Shape : me.Polygon", function () { }); describe("Polygon Bounding Rect", function () { + // get the polygon bounding rect + var boundingRect = stars.getBounds(); + it("width is 188", function () { expect(boundingRect.width).toEqual(188); }); @@ -53,4 +53,21 @@ describe("Shape : me.Polygon", function () { }); }); + describe("Polygon transformation", function () { + var rect = new me.Polygon(0, 0, [ + // draw a star + {x: 0, y: 0}, + {x: 32, y: 0}, + {x: 32, y: 32}, + {x: 0, y: 32} + ]); + var boundingRect = rect.getBounds(); + + it("Bounds should be updated when scaled or rotated", function () { + // scale the polygon and rotate it by 45deg + rect.transform((new me.Matrix2d()).scale(2.0).rotate(Math.PI/4)); + expect(Math.floor(boundingRect.width)).toEqual(90); + expect(Math.floor(boundingRect.height)).toEqual(90); + }); + }); }); diff --git a/tests/spec/sprite-spec.js b/tests/spec/sprite-spec.js index a6cbeeccd8..5183603d2e 100644 --- a/tests/spec/sprite-spec.js +++ b/tests/spec/sprite-spec.js @@ -35,14 +35,4 @@ describe("me.Sprite", function () { expect(bounds.width).toEqual(64); expect(bounds.height).toEqual(64); }); - - /* TODO: add proper bounds testing when transformation are added - it("me.Sprite bounds should be updated when the sprite is scaled or rotated", function () { - var bounds = sprite.getBounds(); - // further scale the sprite (-> 128) and rotate it by 45deg - sprite.transform((new me.Matrix2d()).scale(2.0).rotate(45 * Math.PI/180)); - expect(Math.round(bounds.width)).toEqual(128 + (128 / 2)); - expect(Math.round(bounds.height)).toEqual(128 + (128 / 2)); - }); - */ }); From a1b361bbcf62d8a38d4f7861e7f67edb9194afc5 Mon Sep 17 00:00:00 2001 From: Sanket Mishra Date: Fri, 28 Oct 2016 16:49:41 +0530 Subject: [PATCH 151/177] Update README.md Fixed some typos. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bd4fced94a..a4cab05b87 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ and currently features : - A fresh & lightweight 2D sprite-based engine - Standalone library (does not rely on anything else, except a HTML5 capable browser) -- Compatible with most major browser (Chrome, Safari, Firefox, Opera, IE) and mobile devices +- Compatible with most major browsers (Chrome, Safari, Firefox, Opera, IE) and mobile devices - Device motion & accelerometer support - High DPI & auto scaling - Multi-channel HTML5 audio support and Web Audio on supported devices @@ -38,7 +38,7 @@ and currently features : - Tiled map format version +0.9.x integration for easy level design - Uncompressed Plain, Base64, CSV and JSON encoded XML tilemap loading - Orthogonal, Isometric and Perspective tilemap support - - Multiple layers (multiple background/Foreground, collision and Image layers) + - Multiple layers (multiple background/foreground, collision and Image layers) - Multiple Tileset support - Tileset Transparency settings - Layers Alpha settings From c00e626df270db06adb34ac614be2760ded6176e Mon Sep 17 00:00:00 2001 From: obiot Date: Tue, 1 Nov 2016 12:11:57 +0800 Subject: [PATCH 152/177] add a texture atlas helper function, to clean up a few "FIXME"(s) in the WebGL Renderer did not get that much inspiration for the name :) --- src/video/canvas/texture.js | 19 +++++++++ src/video/texture_cache.js | 19 +++------ src/video/webgl/webgl_renderer.js | 67 ++++++++++--------------------- 3 files changed, 47 insertions(+), 58 deletions(-) diff --git a/src/video/canvas/texture.js b/src/video/canvas/texture.js index a275cef038..641ef65dfb 100644 --- a/src/video/canvas/texture.js +++ b/src/video/canvas/texture.js @@ -130,6 +130,7 @@ }, /** + * build an atlas from the given data * @ignore */ build : function (data) { @@ -161,6 +162,24 @@ return atlas; }, + /** + * build an simple 1 frame atlas from the given data + * @ignore + */ + buildFromFrame : function (width, height, name, repeat) { + return { + "meta" : { + "app" : "melonJS", + "size" : { "w" : width, "h" : height }, + "repeat" : repeat || "no-repeat" + }, + "frames" : [{ + "filename" : name || "default", + "frame" : { "x" : 0, "y" : 0, "w" : width, "h" : height } + }] + }; + }, + /** * build an atlas from the given spritesheet * @ignore diff --git a/src/video/texture_cache.js b/src/video/texture_cache.js index 9a2b261e32..54991bb8a2 100644 --- a/src/video/texture_cache.js +++ b/src/video/texture_cache.js @@ -49,19 +49,12 @@ this.validate(); if (!atlas) { - var w = image.width; - var h = image.height; - atlas = { - // FIXME: Create a texture atlas helper function - "meta" : { - "app" : "melonJS", - "size" : { "w" : w, "h" : h } - }, - "frames" : [{ - "filename" : "default", - "frame" : { "x" : 0, "y" : 0, "w" : w, "h" : h } - }] - }; + atlas = me.CanvasRenderer.prototype.Texture.prototype.buildFromFrame.call( + this, + image.width, + image.height, + image.src ? me.utils.getBasename(image.src) : undefined + ); } var texture = new me.video.renderer.Texture(atlas, image, true); diff --git a/src/video/webgl/webgl_renderer.js b/src/video/webgl/webgl_renderer.js index 155a4fdd38..dd3ad82e05 100644 --- a/src/video/webgl/webgl_renderer.js +++ b/src/video/webgl/webgl_renderer.js @@ -99,22 +99,15 @@ */ createFillTexture : function () { // Create a 1x1 white texture for fill operations - var img = new Uint8Array([255, 255, 255, 255]); + var image = new Uint8Array([255, 255, 255, 255]); /** * @ignore */ - this.fillTexture = new this.Texture({ - // FIXME: Create a texture atlas helper function - "meta" : { - "app" : "melonJS", - "size" : { "w" : 1, "h" : 1 } - }, - "frames" : [{ - "filename" : "default", - "frame" : { "x" : 0, "y" : 0, "w" : 1, "h" : 1 } - }] - }, img); + this.fillTexture = new this.Texture( + me.CanvasRenderer.prototype.Texture.prototype.buildFromFrame.call(this, 1, 1, "fillTexture"), + image + ); this.compositor.uploadTexture( this.fillTexture, @@ -128,7 +121,7 @@ * @ignore */ createFontTexture : function () { - var img = me.video.createCanvas( + var image = me.video.createCanvas( this.backBufferCanvas.width, this.backBufferCanvas.height ); @@ -136,30 +129,20 @@ /** * @ignore */ - this.fontContext2D = this.getContext2d(img); + this.fontContext2D = this.getContext2d(image); /** * @ignore */ - this.fontTexture = new this.Texture({ - // FIXME: Create a texture atlas helper function - "meta" : { - "app" : "melonJS", - "size" : { - "w" : this.backBufferCanvas.width, - "h" : this.backBufferCanvas.height - } - }, - "frames" : [{ - "filename" : "default", - "frame" : { - "x" : 0, - "y" : 0, - "w" : this.backBufferCanvas.width, - "h" : this.backBufferCanvas.height - } - }] - }, img); + this.fontTexture = new this.Texture( + me.CanvasRenderer.prototype.Texture.prototype.buildFromFrame.call( + this, + this.backBufferCanvas.width, + this.backBufferCanvas.height, + "fontTexture" + ), + image + ); this.compositor.uploadTexture(this.fontTexture); }, @@ -180,18 +163,12 @@ * var basic = renderer.createPattern(image, "no-repeat"); */ createPattern : function (image, repeat) { - var texture = new this.Texture({ - // FIXME: Create a texture atlas helper function - "meta" : { - "app" : "melonJS", - "size" : { "w" : image.width, "h" : image.height }, - "repeat" : repeat - }, - "frames" : [{ - "filename" : "default", - "frame" : { "x" : 0, "y" : 0, "w" : image.width, "h" : image.height } - }] - }, image); + var texture = new this.Texture( + me.CanvasRenderer.prototype.Texture.prototype.buildFromFrame.call( + this, image.width, image.height, "pattern", repeat + ), + image + ); // FIXME: Remove old cache entry and texture when changing the repeat mode this.compositor.uploadTexture(texture); From 68b1e3a66bafa9d52e6e291ae5f94842593efe16 Mon Sep 17 00:00:00 2001 From: obiot Date: Tue, 1 Nov 2016 17:38:54 +0800 Subject: [PATCH 153/177] further refactoring to remove other FIXME item(s) https://github.com/melonjs/melonJS/blob/master/src/video/webgl/webgl_renderer.js#L86 --- src/video/canvas/texture.js | 12 ++++++++---- src/video/texture_cache.js | 7 +------ src/video/webgl/compositor.js | 24 ++++++++++++++++-------- src/video/webgl/shader.js | 4 ++-- src/video/webgl/texture.js | 9 +++++++++ src/video/webgl/webgl_renderer.js | 24 +++++++++--------------- 6 files changed, 45 insertions(+), 35 deletions(-) diff --git a/src/video/canvas/texture.js b/src/video/canvas/texture.js index 641ef65dfb..5d3f0776cf 100644 --- a/src/video/canvas/texture.js +++ b/src/video/canvas/texture.js @@ -47,7 +47,7 @@ /** * @ignore */ - init : function (atlas, texture, cached) { + init : function (atlas, texture, cache) { /** * to identify the atlas format (e.g. texture packer) * @ignore @@ -123,9 +123,13 @@ throw new me.video.renderer.Texture.Error("texture atlas format not supported"); } - // Add self to TextureCache - if (!cached) { - me.video.renderer.cache.put(this.texture, this); + // Add self to TextureCache if cache !== false + if (cache !== false) { + if (cache instanceof me.Renderer.TextureCache) { + cache.put(this.texture, this); + } else { + me.video.renderer.cache.put(this.texture, this); + } } }, diff --git a/src/video/texture_cache.js b/src/video/texture_cache.js index 54991bb8a2..a0c1e3b117 100644 --- a/src/video/texture_cache.js +++ b/src/video/texture_cache.js @@ -46,8 +46,6 @@ */ get : function (image, atlas) { if (!this.cache.has(image)) { - this.validate(); - if (!atlas) { atlas = me.CanvasRenderer.prototype.Texture.prototype.buildFromFrame.call( this, @@ -56,10 +54,7 @@ image.src ? me.utils.getBasename(image.src) : undefined ); } - - var texture = new me.video.renderer.Texture(atlas, image, true); - this.cache.set(image, texture); - this.units.set(texture, this.length++); + this.put(image, new me.video.renderer.Texture(atlas, image, false)); } return this.cache.get(image); }, diff --git a/src/video/webgl/compositor.js b/src/video/webgl/compositor.js index 44c3724749..6a069fc054 100644 --- a/src/video/webgl/compositor.js +++ b/src/video/webgl/compositor.js @@ -36,9 +36,7 @@ * @namespace me.WebGLRenderer.Compositor * @memberOf me * @constructor - * @param {WebGLContext} gl Destination WebGL Context - * @param {me.Matrix2d} matrix Global transformation matrix - * @param {me.Color} color Global color + * @param {me.WebGLRenderer} renderer the current WebGL renderer session */ me.WebGLRenderer.Compositor = me.Object.extend( /** @scope me.WebGLRenderer.Compositor.prototype */ @@ -46,7 +44,10 @@ /** * @ignore */ - init : function (gl, matrix, color) { + init : function (renderer) { + // local reference + var gl = renderer.gl; + /** * The number of quads held in the batch * @name length @@ -87,14 +88,20 @@ new me.Vector2d() ]; + // the associated renderer + // TODO : add a set context or whatever function, and split + // the constructor accordingly, so that this is easier to restore + // the GL context when lost + this.renderer = renderer; + // WebGL context - this.gl = gl; + this.gl = renderer.gl; // Global transformation matrix - this.matrix = matrix; + this.matrix = renderer.currentTransform; // Global color - this.color = color; + this.color = renderer.currentColor; // Uniform projection matrix this.uMatrix = new me.Matrix2d(); @@ -214,13 +221,14 @@ * @ignore */ uploadTexture : function (texture, w, h, b, force) { - var unit = me.video.renderer.cache.getUnit(texture); + var unit = this.renderer.cache.getUnit(texture); if (!this.units[unit] || force) { this.units[unit] = true; me.video.shader.createTexture( this.gl, unit, texture.texture, + this.renderer.antiAlias ? this.gl.LINEAR : this.gl.NEAREST, texture.repeat, w, h, diff --git a/src/video/webgl/shader.js b/src/video/webgl/shader.js index b246521fda..d0a461e966 100644 --- a/src/video/webgl/shader.js +++ b/src/video/webgl/shader.js @@ -165,17 +165,17 @@ * @param {WebGLContext} gl WebGL Context * @param {Number} unit Destination texture unit * @param {Image|Canvas|ImageData|UInt8Array[]|Float32Array[]} image Source image + * @param {Number} filter gl.LINEAR or gl.NEAREST * @param {String} [repeat="no-repeat"] Image repeat behavior (see {@link me.ImageLayer#repeat}) * @param {Number} [w] Source image width (Only use with UInt8Array[] or Float32Array[] source image) * @param {Number} [h] Source image height (Only use with UInt8Array[] or Float32Array[] source image) * @param {Number} [b] Source image border (Only use with UInt8Array[] or Float32Array[] source image) * @return {WebGLTexture} A texture object */ - api.createTexture = function (gl, unit, image, repeat, w, h, b) { + api.createTexture = function (gl, unit, image, filter, repeat, w, h, b) { repeat = repeat || "no-repeat"; var texture = gl.createTexture(), - filter = me.video.renderer.antiAlias ? gl.LINEAR : gl.NEAREST, rs = (repeat.search(/^repeat(-x)?$/) === 0) ? gl.REPEAT : gl.CLAMP_TO_EDGE, rt = (repeat.search(/^repeat(-y)?$/) === 0) ? gl.REPEAT : gl.CLAMP_TO_EDGE; diff --git a/src/video/webgl/texture.js b/src/video/webgl/texture.js index b05b76bd19..20bc83463b 100644 --- a/src/video/webgl/texture.js +++ b/src/video/webgl/texture.js @@ -49,6 +49,15 @@ return this._addStMap(atlas, w, h); }, + /** + * @ignore + */ + buildFromFrame : function (width, height, name, repeat) { + var atlas = this._super(me.CanvasRenderer.prototype.Texture, "buildFromFrame", [ width, height, name, repeat ]); + + return this._addStMap(atlas, width, height); + }, + /** * @ignore */ diff --git a/src/video/webgl/webgl_renderer.js b/src/video/webgl/webgl_renderer.js index dd3ad82e05..2a2d4cab14 100644 --- a/src/video/webgl/webgl_renderer.js +++ b/src/video/webgl/webgl_renderer.js @@ -39,7 +39,6 @@ * @memberOf me.WebGLRenderer */ this.gl = this.getContextGL(c, !this.transparent); - var gl = this.gl; /** * @ignore @@ -71,22 +70,15 @@ // Create a compositor var Compositor = options.compositor || me.WebGLRenderer.Compositor; - this.compositor = new Compositor( - gl, - this.currentTransform, - this.currentColor - ); + this.compositor = new Compositor(this); // Create a texture cache this.cache = new me.Renderer.TextureCache( this.compositor.maxTextures ); - // FIXME: Cannot reference me.video.renderer yet - me.video.renderer = this; - - this.createFillTexture(); - this.createFontTexture(); + this.createFillTexture(this.cache); + this.createFontTexture(this.cache); // Configure the WebGL viewport this.scaleCanvas(1, 1); @@ -97,7 +89,7 @@ /** * @ignore */ - createFillTexture : function () { + createFillTexture : function (cache) { // Create a 1x1 white texture for fill operations var image = new Uint8Array([255, 255, 255, 255]); @@ -106,7 +98,8 @@ */ this.fillTexture = new this.Texture( me.CanvasRenderer.prototype.Texture.prototype.buildFromFrame.call(this, 1, 1, "fillTexture"), - image + image, + cache ); this.compositor.uploadTexture( @@ -120,7 +113,7 @@ /** * @ignore */ - createFontTexture : function () { + createFontTexture : function (cache) { var image = me.video.createCanvas( this.backBufferCanvas.width, this.backBufferCanvas.height @@ -141,7 +134,8 @@ this.backBufferCanvas.height, "fontTexture" ), - image + image, + cache ); this.compositor.uploadTexture(this.fontTexture); From 71fbdeb96b39f01774525794a3c4caca9ebabb52 Mon Sep 17 00:00:00 2001 From: obiot Date: Wed, 2 Nov 2016 11:12:18 +0800 Subject: [PATCH 154/177] [WebGL] fix potential context issue when creating texture this probably does not actually fix anything, but it makes the code cleaner. --- src/video/texture_cache.js | 8 +++----- src/video/webgl/texture.js | 5 ++--- src/video/webgl/webgl_renderer.js | 18 ++++++++++-------- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/video/texture_cache.js b/src/video/texture_cache.js index a0c1e3b117..b8c75819a3 100644 --- a/src/video/texture_cache.js +++ b/src/video/texture_cache.js @@ -47,11 +47,9 @@ get : function (image, atlas) { if (!this.cache.has(image)) { if (!atlas) { - atlas = me.CanvasRenderer.prototype.Texture.prototype.buildFromFrame.call( - this, - image.width, - image.height, - image.src ? me.utils.getBasename(image.src) : undefined + atlas = me.video.renderer.Texture.prototype.buildFromFrame.apply( + me.video.renderer.Texture.prototype, + [image.width, image.height, image.src ? me.utils.getBasename(image.src) : undefined] ); } this.put(image, new me.video.renderer.Texture(atlas, image, false)); diff --git a/src/video/webgl/texture.js b/src/video/webgl/texture.js index 20bc83463b..6c2f84a010 100644 --- a/src/video/webgl/texture.js +++ b/src/video/webgl/texture.js @@ -53,9 +53,8 @@ * @ignore */ buildFromFrame : function (width, height, name, repeat) { - var atlas = this._super(me.CanvasRenderer.prototype.Texture, "buildFromFrame", [ width, height, name, repeat ]); - - return this._addStMap(atlas, width, height); + return this._super(me.CanvasRenderer.prototype.Texture, "buildFromFrame", [ width, height, name, repeat ]); + // _addSTMap will be called through build() }, /** diff --git a/src/video/webgl/webgl_renderer.js b/src/video/webgl/webgl_renderer.js index 2a2d4cab14..4ff6409568 100644 --- a/src/video/webgl/webgl_renderer.js +++ b/src/video/webgl/webgl_renderer.js @@ -97,7 +97,10 @@ * @ignore */ this.fillTexture = new this.Texture( - me.CanvasRenderer.prototype.Texture.prototype.buildFromFrame.call(this, 1, 1, "fillTexture"), + this.Texture.prototype.buildFromFrame.apply( + this.Texture.prototype, + [ 1, 1, "fillTexture"] + ), image, cache ); @@ -128,11 +131,9 @@ * @ignore */ this.fontTexture = new this.Texture( - me.CanvasRenderer.prototype.Texture.prototype.buildFromFrame.call( - this, - this.backBufferCanvas.width, - this.backBufferCanvas.height, - "fontTexture" + this.Texture.prototype.buildFromFrame.apply( + this.Texture.prototype, + [ this.backBufferCanvas.width, this.backBufferCanvas.height, "fontTexture"] ), image, cache @@ -158,8 +159,9 @@ */ createPattern : function (image, repeat) { var texture = new this.Texture( - me.CanvasRenderer.prototype.Texture.prototype.buildFromFrame.call( - this, image.width, image.height, "pattern", repeat + this.Texture.prototype.buildFromFrame.apply( + this.Texture.prototype, + [ image.width, image.height, "pattern", repeat] ), image ); From 976850201e7bc80076d58248c41cbd9ba48a5e9e Mon Sep 17 00:00:00 2001 From: obiot Date: Wed, 2 Nov 2016 13:49:46 +0800 Subject: [PATCH 155/177] some (internal) function renaming to avoid confusion on their respective usage --- src/video/canvas/texture.js | 44 +++++++++++++++---------------- src/video/texture_cache.js | 2 +- src/video/webgl/texture.js | 16 +++-------- src/video/webgl/webgl_renderer.js | 6 ++--- 4 files changed, 30 insertions(+), 38 deletions(-) diff --git a/src/video/canvas/texture.js b/src/video/canvas/texture.js index 5d3f0776cf..333610a0f7 100644 --- a/src/video/canvas/texture.js +++ b/src/video/canvas/texture.js @@ -101,7 +101,7 @@ this.repeat = atlas.meta.repeat || "no-repeat"; } // initialize the atlas - this.atlas = this.build(atlas); + this.atlas = this.parse(atlas); } else { // a regular spritesheet ? @@ -113,7 +113,7 @@ atlas.image = texture; } // initialize the atlas - this.atlas = this.buildFromSpriteSheet(atlas); + this.atlas = this.parseFromSpriteSheet(atlas); this.repeat = "no-repeat"; } } @@ -133,11 +133,29 @@ } }, + /** + * create a simple 1 frame texture atlas based on the given parameters + * @ignore + */ + createAtlas : function (width, height, name, repeat) { + return { + "meta" : { + "app" : "melonJS", + "size" : { "w" : width, "h" : height }, + "repeat" : repeat || "no-repeat" + }, + "frames" : [{ + "filename" : name || "default", + "frame" : { "x" : 0, "y" : 0, "w" : width, "h" : height } + }] + }; + }, + /** * build an atlas from the given data * @ignore */ - build : function (data) { + parse : function (data) { var atlas = {}; data.frames.forEach(function (frame) { // fix wrongly formatted JSON (e.g. last dummy object in ShoeBox) @@ -166,29 +184,11 @@ return atlas; }, - /** - * build an simple 1 frame atlas from the given data - * @ignore - */ - buildFromFrame : function (width, height, name, repeat) { - return { - "meta" : { - "app" : "melonJS", - "size" : { "w" : width, "h" : height }, - "repeat" : repeat || "no-repeat" - }, - "frames" : [{ - "filename" : name || "default", - "frame" : { "x" : 0, "y" : 0, "w" : width, "h" : height } - }] - }; - }, - /** * build an atlas from the given spritesheet * @ignore */ - buildFromSpriteSheet : function (data) { + parseFromSpriteSheet : function (data) { var atlas = {}; var image = data.image; var spacing = data.spacing || 0; diff --git a/src/video/texture_cache.js b/src/video/texture_cache.js index b8c75819a3..7f274d0006 100644 --- a/src/video/texture_cache.js +++ b/src/video/texture_cache.js @@ -47,7 +47,7 @@ get : function (image, atlas) { if (!this.cache.has(image)) { if (!atlas) { - atlas = me.video.renderer.Texture.prototype.buildFromFrame.apply( + atlas = me.video.renderer.Texture.prototype.createAtlas.apply( me.video.renderer.Texture.prototype, [image.width, image.height, image.src ? me.utils.getBasename(image.src) : undefined] ); diff --git a/src/video/webgl/texture.js b/src/video/webgl/texture.js index 6c2f84a010..558c0f4479 100644 --- a/src/video/webgl/texture.js +++ b/src/video/webgl/texture.js @@ -41,10 +41,10 @@ /** * @ignore */ - build : function (data) { + parse : function (data) { var w = data.meta.size.w; var h = data.meta.size.h; - var atlas = this._super(me.CanvasRenderer.prototype.Texture, "build", [ data ]); + var atlas = this._super(me.CanvasRenderer.prototype.Texture, "parse", [ data ]); return this._addStMap(atlas, w, h); }, @@ -52,18 +52,10 @@ /** * @ignore */ - buildFromFrame : function (width, height, name, repeat) { - return this._super(me.CanvasRenderer.prototype.Texture, "buildFromFrame", [ width, height, name, repeat ]); - // _addSTMap will be called through build() - }, - - /** - * @ignore - */ - buildFromSpriteSheet : function (data) { + parseFromSpriteSheet : function (data) { var w = data.image.width; var h = data.image.height; - var atlas = this._super(me.CanvasRenderer.prototype.Texture, "buildFromSpriteSheet", [ data ]); + var atlas = this._super(me.CanvasRenderer.prototype.Texture, "parseFromSpriteSheet", [ data ]); return this._addStMap(atlas, w, h); }, diff --git a/src/video/webgl/webgl_renderer.js b/src/video/webgl/webgl_renderer.js index 4ff6409568..cabc49d3c4 100644 --- a/src/video/webgl/webgl_renderer.js +++ b/src/video/webgl/webgl_renderer.js @@ -97,7 +97,7 @@ * @ignore */ this.fillTexture = new this.Texture( - this.Texture.prototype.buildFromFrame.apply( + this.Texture.prototype.createAtlas.apply( this.Texture.prototype, [ 1, 1, "fillTexture"] ), @@ -131,7 +131,7 @@ * @ignore */ this.fontTexture = new this.Texture( - this.Texture.prototype.buildFromFrame.apply( + this.Texture.prototype.createAtlas.apply( this.Texture.prototype, [ this.backBufferCanvas.width, this.backBufferCanvas.height, "fontTexture"] ), @@ -159,7 +159,7 @@ */ createPattern : function (image, repeat) { var texture = new this.Texture( - this.Texture.prototype.buildFromFrame.apply( + this.Texture.prototype.createAtlas.apply( this.Texture.prototype, [ image.width, image.height, "pattern", repeat] ), From 02e8a27edef763d88665557bedb880869a1bb766 Mon Sep 17 00:00:00 2001 From: obiot Date: Tue, 8 Nov 2016 15:26:07 +0800 Subject: [PATCH 156/177] [#828] replace JSHint with ESLint --- .eslintrc.json | 168 ++++++++++++++++++++++++++++ .jshintrc | 117 ------------------- Gruntfile.js | 62 +++++----- package.json | 2 +- plugins/debug/debugPanel.js | 18 +-- plugins/debug/particleDebugPanel.js | 4 +- src/audio/audio.js | 1 - src/config.js | 2 +- src/entity/entity.js | 2 +- src/input/pointer.js | 4 +- src/lang/array.js | 3 + src/lang/function.js | 3 + src/lang/number.js | 3 + src/lang/object.js | 3 + src/lang/string.js | 5 +- src/lang/window.js | 3 + src/level/TMXRenderer.js | 6 +- src/level/TMXTile.js | 4 +- src/level/TMXTiledMap.js | 2 +- src/level/TMXUtils.js | 1 + src/math/observable_vector2.js | 4 +- src/math/observable_vector3.js | 8 +- src/physics/body.js | 2 +- src/physics/collision.js | 2 +- src/renderable/container.js | 6 +- src/renderable/sprite.js | 2 +- src/vendors/es6-collections.js | 11 +- src/vendors/es6-promise.js | 13 +-- src/vendors/howler.core.js | 7 +- src/vendors/tween.js | 9 +- src/video/canvas/texture.js | 4 +- src/video/renderer.js | 2 +- src/video/webgl/compositor.js | 2 + src/video/webgl/shader.js | 6 +- src/video/webgl/webgl_renderer.js | 2 +- tests/spec/bitmapfontdata-spec.js | 2 +- tests/spec/draggable-spec.js | 6 +- tests/spec/droptarget-spec.js | 3 + tests/spec/es6-collections.js | 9 +- tests/spec/es6-promise.js | 6 +- tests/spec/inheritance-spec.js | 6 +- tests/spec/observableVect2d-spec.js | 52 ++++----- tests/spec/observableVect3d-spec.js | 68 +++++------ tests/spec/vect2d-spec.js | 44 ++++---- tests/spec/vect3d-spec.js | 56 +++++----- 45 files changed, 395 insertions(+), 350 deletions(-) create mode 100644 .eslintrc.json delete mode 100644 .jshintrc diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000000..697f1da15d --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,168 @@ +{ + "env": { + "es6": true, + "browser": true + }, + + "globals": { + // Custom Globals + + // Non-standard + "Windows" : false, + "define" : false, + + // melonJS + "me" : false, + "throttle" : false, + "__QUAD_VERTEX__" : false, + "__QUAD_FRAGMENT__" : false, + "__LINE_VERTEX__" : false, + "__LINE_FRAGMENT__" : false, + + // Howler + "AudioContext" : false, + "webkitAudioContext" : false, + "Howl" : false, + "Howler" : false, + + // Jasmine + "jasmine" : false, + "describe" : false, + "it" : false, + "expect" : false, + "runs" : false, + "waitsFor" : false, + "beforeEach" : false, + "afterEach" : false, + "spyOn" : false, + "xdescribe" : false, + "xit" : false + }, + + "rules": { + + // Severity should be one of the following: 0 = off, 1 = warn, 2 = error + // http://eslint.org/docs/rules/ + + "accessor-pairs": 2, + "arrow-spacing": [2, { "before": true, "after": true }], + "block-spacing": [2, "always"], + "brace-style": [0, "1tbs", { "allowSingleLine": true }], + "camelcase": [0, { "properties": "never" }], + "comma-dangle": [2, "never"], + "comma-spacing": [2, { "before": false, "after": true }], + "comma-style": [2, "last"], + "constructor-super": 2, + "curly": [2, "multi-line"], + "dot-location": [2, "property"], + "eol-last": 2, + "eqeqeq": [2, "allow-null"], + "func-call-spacing": [2, "never"], + "handle-callback-err": [2, "^(err|error)$" ], + "indent": [0, 2, { "SwitchCase": 1 }], + "key-spacing": [0, { "beforeColon": false, "afterColon": true }], + "keyword-spacing": [2, { "before": true, "after": true }], + "new-cap": [2, { "newIsCap": true, "capIsNew": false }], + "new-parens": 2, + "no-array-constructor": 2, + "no-caller": 2, + "no-class-assign": 2, + "no-cond-assign": 2, + "no-const-assign": 2, + "no-constant-condition": [2, { "checkLoops": false }], + "no-control-regex": 2, + "no-debugger": 2, + "no-delete-var": 2, + "no-dupe-args": 2, + "no-dupe-class-members": 2, + "no-dupe-keys": 2, + "no-duplicate-case": 2, + "no-duplicate-imports": 2, + "no-empty-character-class": 2, + "no-empty-pattern": 2, + "no-eval": 2, + "no-ex-assign": 2, + "no-extend-native": 2, + "no-extra-bind": 2, + "no-extra-boolean-cast": 2, + "no-extra-parens": [0, "functions"], + "no-fallthrough": 2, + "no-floating-decimal": 2, + "no-func-assign": 2, + "no-global-assign": 2, + "no-implied-eval": 2, + "no-inner-declarations": [2, "functions"], + "no-invalid-regexp": 2, + "no-irregular-whitespace": 2, + "no-iterator": 2, + "no-label-var": 2, + "no-labels": [2, { "allowLoop": false, "allowSwitch": false }], + "no-lone-blocks": 2, + "no-mixed-spaces-and-tabs": 2, + "no-multi-spaces": 0, + "no-multi-str": 2, + "no-multiple-empty-lines": [0, { "max": 1 }], + "no-native-reassign": 2, + "no-negated-in-lhs": 2, + "no-new": 2, + "no-new-func": 2, + "no-new-object": 2, + "no-new-require": 2, + "no-new-symbol": 2, + "no-new-wrappers": 2, + "no-obj-calls": 2, + "no-octal": 2, + "no-octal-escape": 2, + "no-path-concat": 2, + "no-proto": 2, + "no-redeclare": 2, + "no-regex-spaces": 2, + "no-return-assign": [2, "except-parens"], + "no-self-assign": 2, + "no-self-compare": 2, + "no-sequences": 0, + "no-shadow-restricted-names": 2, + "no-sparse-arrays": 2, + "no-tabs": 2, + "no-template-curly-in-string": 2, + "no-this-before-super": 2, + "no-throw-literal": 0, + "no-trailing-spaces": 0, + "no-undef": 2, + "no-undef-init": 2, + "no-unexpected-multiline": 2, + "no-unmodified-loop-condition": 2, + "no-unneeded-ternary": [0, { "defaultAssignment": false }], + "no-unreachable": 2, + "no-unsafe-finally": 2, + "no-unsafe-negation": 2, + "no-unused-vars": [2, { "vars": "all", "args": "none" }], + "no-useless-call": 0, + "no-useless-computed-key": 2, + "no-useless-constructor": 2, + "no-useless-escape": 0, + "no-useless-rename": 2, + "no-whitespace-before-property": 2, + "no-with": 2, + "object-property-newline": [2, { "allowMultiplePropertiesPerLine": true }], + "one-var": [0, { "initialized": "never" }], + "operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }], + "padded-blocks": [0, "never"], + "quotes": [2, "double", { "avoidEscape": true, "allowTemplateLiterals": true }], + "rest-spread-spacing": [2, "never"], + "semi": [0, "never"], + "semi-spacing": [2, { "before": false, "after": true }], + "space-before-blocks": [2, "always"], + "space-before-function-paren": [0, "always"], + "space-in-parens": "off", // [2, "never"], + "space-infix-ops": 0, + "space-unary-ops": [0, { "words": true, "nonwords": false }], + "spaced-comment": [0, "always", { "line": { "markers": ["*package", "!", ","] }, "block": { "balanced": true, "markers": ["*package", "!", ","], "exceptions": ["*"] } }], + "template-curly-spacing": [2, "never"], + "unicode-bom": [2, "never"], + "use-isnan": 2, + "valid-typeof": 2, + "wrap-iife": [2, "any", { "functionPrototypeMethods": true }], + "yield-star-spacing": [2, "both"], + "yoda": [2, "never"] } +} diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index e39fe3a828..0000000000 --- a/.jshintrc +++ /dev/null @@ -1,117 +0,0 @@ -{ - // JSHint Default Configuration File (as on JSHint website) - // See http://jshint.com/docs/ for more details - - "maxerr" : 50, // {int} Maximum error before stopping - - // Enforcing - "bitwise" : false, // true: Prohibit bitwise operators (&, |, ^, etc.) - "camelcase" : false, // true: Identifiers must be in camelCase - "curly" : true, // true: Require {} for every new block or scope - "eqeqeq" : true, // true: Require triple equals (===) for comparison - "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty() - "immed" : true, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` - "indent" : 4, // {int} Number of spaces to use for indentation - "latedef" : true, // true: Require variables/functions to be defined before being used - "newcap" : true, // true: Require capitalization of all constructor functions e.g. `new F()` - "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` - "noempty" : true, // true: Prohibit use of empty blocks - "nonew" : true, // true: Prohibit use of constructors for side-effects (without assignment) - "plusplus" : false, // true: Prohibit use of `++` & `--` - "quotmark" : "double", // Quotation mark consistency: - // false : do nothing (default) - // true : ensure whatever is used is consistent - // "single" : require single quotes - // "double" : require double quotes - "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) - "unused" : true, // true: Require all defined variables be used - "strict" : false, // true: Requires all functions run in ES5 Strict Mode - "trailing" : true, // true: Prohibit trailing whitespaces - "maxparams" : false, // {int} Max number of formal params allowed per function - "maxdepth" : false, // {int} Max depth of nested blocks (within functions) - "maxstatements" : false, // {int} Max number statements per function - "maxcomplexity" : false, // {int} Max cyclomatic complexity per function - "maxlen" : false, // {int} Max number of characters per line - - // Relaxing - "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) - "boss" : false, // true: Tolerate assignments where comparisons would be expected - "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. - "eqnull" : true, // true: Tolerate use of `== null` - "esnext" : true, // true: Allow ES.next (ES6) syntax (ex: `const`) - "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) - // (ex: `for each`, multiple try/catch, function expression) - "evil" : true, // true: Tolerate use of `eval` and `new Function()` - "expr" : false, // true: Tolerate `ExpressionStatement` as Programs - "funcscope" : false, // true: Tolerate defining variables inside control statements - "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') - "iterator" : false, // true: Tolerate using the `__iterator__` property - "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block - "laxbreak" : false, // true: Tolerate possibly unsafe line breakings - "laxcomma" : false, // true: Tolerate comma-first style coding - "loopfunc" : false, // true: Tolerate functions being defined in loops - "multistr" : false, // true: Tolerate multi-line strings - "notypeof " : false, // true: Disable the check on the use of the typeof operator (result vs given string) - "proto" : false, // true: Tolerate using the `__proto__` property - "scripturl" : false, // true: Tolerate script-targeted URLs - "smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment - "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` - "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation - "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` - "validthis" : false, // true: Tolerate using this in a non-constructor function - - // Environments - "browser" : true, // Web Browser (window, document, etc) - "couch" : false, // CouchDB - "devel" : true, // Development/debugging (alert, confirm, etc) - "dojo" : false, // Dojo Toolkit - "jquery" : false, // jQuery - "mootools" : false, // MooTools - "node" : true, // Node.js - "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) - "prototypejs" : false, // Prototype and Scriptaculous - "rhino" : false, // Rhino - "worker" : false, // Web Workers - "wsh" : false, // Windows Scripting Host - "yui" : false, // Yahoo User Interface - - // Legacy - "nomen" : false, // true: Prohibit dangling `_` in variables - "onevar" : false, // true: Allow only one `var` statement per function - "passfail" : false, // true: Stop on first error - "white" : true, // true: Check against strict whitespace and indentation rules - - // Custom Globals - "globals" : { - // Non-standard - "Windows" : false, - "define" : false, - - // melonJS - "me" : false, - "throttle" : false, - "__QUAD_VERTEX__" : false, - "__QUAD_FRAGMENT__" : false, - "__LINE_VERTEX__" : false, - "__LINE_FRAGMENT__" : false, - - // Howler - "AudioContext" : false, - "webkitAudioContext" : false, - "Howl" : false, - "Howler" : false, - - // Jasmine - "jasmine" : false, - "describe" : false, - "it" : false, - "expect" : false, - "runs" : false, - "waitsFor" : false, - "beforeEach" : false, - "afterEach" : false, - "spyOn" : false, - "xdescribe" : false, - "xit" : false - } -} diff --git a/Gruntfile.js b/Gruntfile.js index 2bdcb09a63..07b2109414 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,6 +1,8 @@ +// eslint-disable-next-line module.exports = function (grunt) { "use strict"; + var sourceFiles = grunt.file.readJSON("sourceFiles.json"); var testSpecs = grunt.file.readJSON("testSpecs.json"); @@ -32,7 +34,7 @@ module.exports = function (grunt) { "__QUAD_FRAGMENT__" : quadFragment, "__QUAD_VERTEX__" : quadVertex, "__LINE_FRAGMENT__" : lineFragment, - "__LINE_VERTEX__" : lineVertex, + "__LINE_VERTEX__" : lineVertex }, usePrefix : false, force : true, @@ -40,8 +42,8 @@ module.exports = function (grunt) { { match : /this\._super\(\s*([\w\.]+)\s*,\s*"(\w+)"\s*(,\s*)?/g, replacement : "$1.prototype.$2.apply(this$3" - }, - ], + } + ] }, files : [ { @@ -93,8 +95,8 @@ module.exports = function (grunt) { // Remove unnecessary whitespace match : /\s*([;,[\](){}\\\/\-+*|^&!=<>?~%])\s*/g, replacement : "$1" - }, - ], + } + ] }, files : [ { @@ -110,7 +112,7 @@ module.exports = function (grunt) { dot : { glsl : { options : { - strip : false, + strip : false }, files : [ { @@ -139,21 +141,21 @@ module.exports = function (grunt) { } }, - jshint : { - options : { - jshintrc : ".jshintrc" - }, + eslint : { + options : { + configFile : ".eslintrc.json" + }, - beforeConcat : { - files : { - src : [ - testSpecs, - sourceFiles, - "Gruntfile.js", - "plugins/**/*" - ] - } - }, + beforeConcat : { + files : { + src : [ + testSpecs, + sourceFiles, + "Gruntfile.js", + "plugins/**/*" + ] + } + }, afterConcat : { files : { @@ -224,9 +226,9 @@ module.exports = function (grunt) { "docs/**", "examples/**", "media/logo.png", - "plugins/**", + "plugins/**" ], - dest : "dist/", + dest : "dist/" } }, @@ -236,10 +238,10 @@ module.exports = function (grunt) { dist : "dist", build_branch : "gh-pages", exclude : [ "node_modules/" ], - pull : true, - }, - }, - }, + pull : true + } + } + } }); grunt.loadNpmTasks("grunt-contrib-clean"); @@ -247,7 +249,7 @@ module.exports = function (grunt) { grunt.loadNpmTasks("grunt-contrib-connect"); grunt.loadNpmTasks("grunt-contrib-copy"); grunt.loadNpmTasks("grunt-contrib-jasmine"); - grunt.loadNpmTasks("grunt-contrib-jshint"); + grunt.loadNpmTasks("grunt-eslint"); grunt.loadNpmTasks("grunt-contrib-uglify"); grunt.loadNpmTasks("grunt-build-gh-pages"); grunt.loadNpmTasks("grunt-jsdoc"); @@ -261,11 +263,11 @@ module.exports = function (grunt) { grunt.registerTask("build", [ "lint", "uglify" ]); grunt.registerTask("glsl", [ "dot:glsl", "replace:glsl" ]); grunt.registerTask("lint", [ - "jshint:beforeConcat", + "eslint:beforeConcat", "glsl", "concat", "replace:dist", - "jshint:afterConcat", + "eslint:afterConcat" ]); grunt.registerTask("doc", [ "replace:docs", "jsdoc" ]); grunt.registerTask("test", [ "lint", "connect:server", "jasmine" ]); @@ -275,7 +277,7 @@ module.exports = function (grunt) { "build", "doc", "copy:dist", - "buildGhPages:dist", + "buildGhPages:dist" ]); grunt.registerTask("release", [ "gh-pages", "dorelease" ]); }; diff --git a/package.json b/package.json index de53a9486c..9fa704a54e 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "grunt-contrib-connect": "^0.11.2", "grunt-contrib-copy": "^0.8.2", "grunt-contrib-jasmine": "^1.0.0", - "grunt-contrib-jshint": "^0.11.3", + "grunt-eslint": "^19.0.0", "grunt-contrib-uglify": "^1.0.1", "grunt-jsdoc": "^1.1.0", "grunt-replace": "^0.11.0", diff --git a/plugins/debug/debugPanel.js b/plugins/debug/debugPanel.js index 1dc75eb659..d6f5e408c4 100644 --- a/plugins/debug/debugPanel.js +++ b/plugins/debug/debugPanel.js @@ -132,8 +132,7 @@ this.keyHandler = me.event.subscribe(me.event.KEYDOWN, function (action) { if (action === "show") { self.show(); - } - else if (action === "hide") { + } else if (action === "hide") { self.hide(); } }); @@ -384,8 +383,7 @@ update : function () { if (me.input.isKeyPressed("show")) { this.show(); - } - else if (me.input.isKeyPressed("hide")) { + } else if (me.input.isKeyPressed("hide")) { this.hide(); } return true; @@ -396,13 +394,11 @@ // check the clickable areas if (this.area.renderHitBox.containsPoint(e.gameX, e.gameY)) { me.debug.renderHitBox = !me.debug.renderHitBox; - } - else if (this.area.renderVelocity.containsPoint(e.gameX, e.gameY)) { + } else if (this.area.renderVelocity.containsPoint(e.gameX, e.gameY)) { // does nothing for now, since velocity is // rendered together with hitboxes (is a global debug flag required?) me.debug.renderVelocity = !me.debug.renderVelocity; - } - else if (this.area.renderQuadTree.containsPoint(e.gameX, e.gameY)) { + } else if (this.area.renderQuadTree.containsPoint(e.gameX, e.gameY)) { me.debug.renderQuadTree = !me.debug.renderQuadTree; } // force repaint @@ -423,8 +419,7 @@ renderer.fillRect(bounds.pos.x, bounds.pos.y, bounds.width, bounds.height); renderer.restore(); } - } - else { + } else { //has subnodes? drawQuadtree them! for (var i = 0; i < node.nodes.length; i++) { this.drawQuadTreeNode(renderer, node.nodes[i]); @@ -462,8 +457,7 @@ renderer.fillRect(this.memoryPositionX + 1, 1, len - 1, 17); this.font.draw(renderer, "Heap : " + usedHeap + "/" + totalHeap + " MB", this.memoryPositionX + 5, 5 * this.mod); - } - else { + } else { // Heap Memory information not available this.font.draw(renderer, "Heap : ??/?? MB", this.memoryPositionX, 5 * this.mod); } diff --git a/plugins/debug/particleDebugPanel.js b/plugins/debug/particleDebugPanel.js index b8156834ae..ac3774f4b1 100644 --- a/plugins/debug/particleDebugPanel.js +++ b/plugins/debug/particleDebugPanel.js @@ -243,7 +243,7 @@ // draw the panel renderer.setGlobalAlpha(0.5); renderer.setColor("black"); - renderer.fillRect(this._bounds.left, this._bounds.top, + renderer.fillRect(this._bounds.left, this._bounds.top, this._bounds.width, this._bounds.height); renderer.setGlobalAlpha(1.0); renderer.translate(this._bounds.left, this._bounds.top); @@ -267,7 +267,7 @@ this.drawGraph(renderer); renderer.restore(); - }, + } }); /** diff --git a/src/audio/audio.js b/src/audio/audio.js index a0542a6707..cecd0cbd10 100644 --- a/src/audio/audio.js +++ b/src/audio/audio.js @@ -511,7 +511,6 @@ * me.audio.unload("awesome_music"); */ api.unload = function (sound_name) { - sound_name = sound_name; if (!(sound_name in audioTracks)) { return false; } diff --git a/src/config.js b/src/config.js index 1877efc11b..30f6dc0dfc 100644 --- a/src/config.js +++ b/src/config.js @@ -30,7 +30,7 @@ * @memberOf me.sys */ fps : 60, - + /** * Rate at which the game updates;
* must be equal to or lower than the fps diff --git a/src/entity/entity.js b/src/entity/entity.js index 81b1abadf6..02b6cbab28 100644 --- a/src/entity/entity.js +++ b/src/entity/entity.js @@ -60,7 +60,7 @@ "frameheight" : ~~(settings.frameheight || settings.height), "spacing" : ~~settings.spacing, "margin" : ~~settings.margin, - "anchorPoint" : settings.anchorPoint, + "anchorPoint" : settings.anchorPoint }); } diff --git a/src/input/pointer.js b/src/input/pointer.js index f5484c1b5f..bb382769e5 100644 --- a/src/input/pointer.js +++ b/src/input/pointer.js @@ -321,7 +321,7 @@ var candidates = me.collision.quadTree.retrieve(currentPointer, me.Container.prototype._sortReverseZ); // add the viewport to the list of candidates - candidates.push ( me.game.viewport ); + candidates.push( me.game.viewport ); for (var c = candidates.length, candidate; c--, (candidate = candidates[c]);) { @@ -713,7 +713,7 @@ evtHandlers.set(region, { region : region, callbacks : {}, - pointerId : null, + pointerId : null }); } diff --git a/src/lang/array.js b/src/lang/array.js index c53d019d33..e1c741da68 100644 --- a/src/lang/array.js +++ b/src/lang/array.js @@ -10,6 +10,8 @@ * @see {@link https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array|Array} */ +/* eslint-disable no-extend-native */ + /** * Remove the specified object from the Array
* @memberof! external:Array# @@ -141,3 +143,4 @@ window.Uint16Array = window.Uint16Array || me.TypedArray; * @see {@link https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Uint32Array|Uint32Array} */ window.Uint32Array = window.Uint32Array || me.TypedArray; +/* eslint-enable no-extend-native */ diff --git a/src/lang/function.js b/src/lang/function.js index e092309a40..681ddb567a 100644 --- a/src/lang/function.js +++ b/src/lang/function.js @@ -4,6 +4,8 @@ * http://www.melonjs.org */ +/* eslint-disable no-extend-native */ + /** * The built in Function Object * @external Function @@ -73,3 +75,4 @@ if (!Function.prototype.bind) { Function.prototype.defer = function () { return setTimeout(this.bind.apply(this, arguments), 0.01); }; +/* eslint-enable no-extend-native */ diff --git a/src/lang/number.js b/src/lang/number.js index 2dfb12b35b..8ed81c2fad 100644 --- a/src/lang/number.js +++ b/src/lang/number.js @@ -10,6 +10,8 @@ * @see {@link https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Number|Number} */ +/* eslint-disable no-extend-native */ + /** * add a clamp fn to the Number object * @memberof! external:Number# @@ -154,3 +156,4 @@ Number.prototype.degToRad = function (angle) { Number.prototype.radToDeg = function (angle) { return (angle || this) * (180.0 / Math.PI); }; +/* eslint-enable no-extend-native */ diff --git a/src/lang/object.js b/src/lang/object.js index e4c503b8ea..039d037aed 100644 --- a/src/lang/object.js +++ b/src/lang/object.js @@ -10,6 +10,8 @@ * @see {@link https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object|Object} */ +/* eslint-disable no-self-compare */ + if (!Object.defineProperty) { /** * simple defineProperty function definition (if not supported by the browser)
@@ -314,3 +316,4 @@ if (!Object.assign) { */ me.Object = Jay; })(); +/* eslint-enable no-self-compare */ diff --git a/src/lang/string.js b/src/lang/string.js index 282f7e94bd..52ff6f0333 100644 --- a/src/lang/string.js +++ b/src/lang/string.js @@ -10,12 +10,14 @@ * @see {@link https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String|String} */ +/* eslint-disable no-extend-native, yoda */ + if (!String.prototype.trim) { /** * removes whitespace from both ends of a string. Whitespace in this context is all the whitespace characters (space, tab, no-break space, etc.) and all the line terminator characters (LF, CR, etc.). * @memberof! external:String# * @alias trim - * @return {string} the string stripped of whitespace from both ends. + * @return {string} the string stripped of whitespace from both ends. */ if (!String.prototype.trim) { (function() { @@ -101,3 +103,4 @@ String.prototype.toHex = function () { } return res; }; +/* eslint-enable no-extend-native */ diff --git a/src/lang/window.js b/src/lang/window.js index d8d46b8c3d..9c8bbcc4bb 100644 --- a/src/lang/window.js +++ b/src/lang/window.js @@ -4,6 +4,8 @@ * http://www.melonjs.org */ +/* eslint-disable space-before-blocks, no-global-assign, no-native-reassign */ + (function () { /** @@ -207,3 +209,4 @@ } })(); +/* eslint-enable space-before-blocks, no-global-assign, no-native-reassign */ diff --git a/src/level/TMXRenderer.js b/src/level/TMXRenderer.js index 92ad33e282..4db14c5667 100644 --- a/src/level/TMXRenderer.js +++ b/src/level/TMXRenderer.js @@ -14,13 +14,13 @@ {x: 0, y: 0}, {x: + 1, y: - 1}, {x: + 1, y: 0}, - {x: + 2, y: 0}, + {x: + 2, y: 0} ]; var offsetsStaggerY = [ {x: 0, y: 0}, {x: - 1, y: + 1}, {x: 0, y: + 1}, - {x: 0, y: + 2}, + {x: 0, y: + 2} ]; /** @@ -336,7 +336,7 @@ var columnItr = rowItr.clone(); // main drawing loop - for (var y = startPos.y * 2 ; y - this.tileheight * 2 < rectEnd.y * 2; y += this.tileheight) { + for (var y = startPos.y * 2; y - this.tileheight * 2 < rectEnd.y * 2; y += this.tileheight) { columnItr.setV(rowItr); for (var x = startPos.x; x < rectEnd.x; x += this.tilewidth) { //check if it's valid tile, if so render diff --git a/src/level/TMXTile.js b/src/level/TMXTile.js index 04ac020c1e..6b536db71c 100644 --- a/src/level/TMXTile.js +++ b/src/level/TMXTile.js @@ -151,7 +151,7 @@ (tileset.animations.get(this.tileId).frames).forEach(function (frame) { frameId.push(frame.tileid); frames.push({ - name : ""+frame.tileid, + name : "" + frame.tileid, delay : frame.duration }); }); @@ -172,6 +172,6 @@ } return renderable; - }, + } }); })(); diff --git a/src/level/TMXTiledMap.js b/src/level/TMXTiledMap.js index c4d56f4099..d21803793e 100644 --- a/src/level/TMXTiledMap.js +++ b/src/level/TMXTiledMap.js @@ -59,7 +59,7 @@ function setLayerData(layer, data) { var idx = 0; // set everything - for (var y = 0 ; y < layer.rows; y++) { + for (var y = 0; y < layer.rows; y++) { for (var x = 0; x < layer.cols; x++) { // get the value of the gid var gid = data[idx++]; diff --git a/src/level/TMXUtils.js b/src/level/TMXUtils.js index fb286683ff..4193e6114e 100644 --- a/src/level/TMXUtils.js +++ b/src/level/TMXUtils.js @@ -69,6 +69,7 @@ // try to evaluate it match = value.split(/^eval:/i)[1]; try { + // eslint-disable-next-line value = eval(match); } catch (e) { diff --git a/src/math/observable_vector2.js b/src/math/observable_vector2.js index f7f5ebc67a..85562c0d34 100644 --- a/src/math/observable_vector2.js +++ b/src/math/observable_vector2.js @@ -88,7 +88,7 @@ this._y = y; return this; }, - + /** * set the callback to be executed when the vector is changed * @name setCallback @@ -106,7 +106,7 @@ this.onUpdate = fn; return this; }, - + /** * Add the passed vector to this vector * @name add diff --git a/src/math/observable_vector3.js b/src/math/observable_vector3.js index 5c14a36045..56fda10b39 100644 --- a/src/math/observable_vector3.js +++ b/src/math/observable_vector3.js @@ -95,7 +95,7 @@ this._z = z; return this; }, - + /** * set the vector value without triggering the callback * @name setMuted @@ -112,7 +112,7 @@ this._z = z; return this; }, - + /** * set the callback to be executed when the vector is changed * @name setCallback @@ -130,7 +130,7 @@ this.onUpdate = fn; return this; }, - + /** * Add the passed vector to this vector * @name add @@ -369,7 +369,7 @@ * @return {me.ObservableVector3d} Reference to this object for method chaining */ copy : function (v) { - return this._set(v.x, v.y,typeof (v.z) !== "undefined" ? v.z : this._z); + return this._set(v.x, v.y, typeof (v.z) !== "undefined" ? v.z : this._z); }, /** diff --git a/src/physics/body.js b/src/physics/body.js index df13315f39..9fdb46fa87 100644 --- a/src/physics/body.js +++ b/src/physics/body.js @@ -386,7 +386,7 @@ this.pos.setV(_bounds.pos); this.resize(_bounds.width, _bounds.height); - for (var i = 1 ; i < this.shapes.length; i++) { + for (var i = 1; i < this.shapes.length; i++) { this.union(this.shapes[i].getBounds()); } } diff --git a/src/physics/collision.js b/src/physics/collision.js index 8bac417b41..96a2a7acaa 100644 --- a/src/physics/collision.js +++ b/src/physics/collision.js @@ -524,7 +524,7 @@ } // If any of the edge normals of B is a separating axis, no intersection. - for (i = 0;i < bLen; i++) { + for (i = 0; i < bLen; i++) { if (isSeparatingAxis(posA, posB, aPoints, bPoints, bNormals[i], response)) { T_VECTORS.push(posA); T_VECTORS.push(posB); diff --git a/src/renderable/container.js b/src/renderable/container.js index fa95d701e5..9b37d41f1a 100644 --- a/src/renderable/container.js +++ b/src/renderable/container.js @@ -450,8 +450,7 @@ }, onActivateEvent : function () { - for (var i = this.children.length, obj; i--, (obj = this.children[i]);) { - var child = this.children[i]; + for (var i = this.children.length, child; i--, (child = this.children[i]);) { if (typeof child.onActivateEvent === "function") { child.onActivateEvent(); } @@ -628,8 +627,7 @@ }, onDeactivateEvent : function () { - for (var i = this.children.length, obj; i--, (obj = this.children[i]);) { - var child = this.children[i]; + for (var i = this.children.length, child; i--, (child = this.children[i]);) { if (typeof child.onDeactivateEvent === "function") { child.onDeactivateEvent(); } diff --git a/src/renderable/sprite.js b/src/renderable/sprite.js index fc455c503c..48ccbbf869 100644 --- a/src/renderable/sprite.js +++ b/src/renderable/sprite.js @@ -375,7 +375,7 @@ this.current = this.anim[name]; this.resetAnim = resetAnim || null; this.setAnimationFrame(this.current.idx); - // XXX this should not be overwritten + // XXX this should not be overwritten this.current.name = name; if (!_preserve_dt) { this.dt = 0; diff --git a/src/vendors/es6-collections.js b/src/vendors/es6-collections.js index 03a9f49ea6..bebb4e51b0 100644 --- a/src/vendors/es6-collections.js +++ b/src/vendors/es6-collections.js @@ -21,15 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* jshint -W003 */ -/* jshint -W013 */ -/* jshint -W015 */ -/* jshint -W030 */ -/* jshint -W035 */ -/* jshint -W040 */ -/* jshint -W058 */ -/* jshint -W108 */ -/* jshint -W116 */ +/* eslint-disable space-before-blocks, quotes, block-spacing, new-parens, comma-spacing, yoda, curly, no-self-compare, pace-infix-ops, new-cap, keyword-spacing, no-redeclare, no-undef, no-new, eqeqeq */ (function (exports) {'use strict'; //shared pointer @@ -258,3 +250,4 @@ THE SOFTWARE. } })(typeof exports != 'undefined' && typeof global != 'undefined' ? global : window ); +/* eslint-enable space-before-blocks, quotes, block-spacing, new-parens, comma-spacing, yoda, curly, no-self-compare, pace-infix-ops, new-cap, keyword-spacing, no-redeclare, no-undef, no-new, eqeqeq */ diff --git a/src/vendors/es6-promise.js b/src/vendors/es6-promise.js index d0c7f98c75..3007df824e 100644 --- a/src/vendors/es6-promise.js +++ b/src/vendors/es6-promise.js @@ -6,17 +6,7 @@ * @version 3.2.1 */ -/* jshint -W003 */ -/* jshint -W030 */ -/* jshint -W033 */ -/* jshint -W035 */ -/* jshint -W040 */ -/* jshint -W055 */ -/* jshint -W064 */ -/* jshint -W069 */ -/* jshint -W098 */ -/* jshint -W108 */ -/* jshint -W117 */ +/* eslint-disable no-unused-vars, no-new-func, space-before-blocks, quotes, space-infix-ops, new-cap, keyword-spacing, no-redeclare, no-undef, no-new*/ (function() { "use strict"; @@ -968,3 +958,4 @@ lib$es6$promise$polyfill$$default(); }).call(this); +/* eslint-enable no-unused-vars, no-new-func, space-before-blocks, quotes, space-infix-ops, new-cap, keyword-spacing, no-redeclare, no-undef, no-new */ diff --git a/src/vendors/howler.core.js b/src/vendors/howler.core.js index 1267a60ac9..5662981f3e 100644 --- a/src/vendors/howler.core.js +++ b/src/vendors/howler.core.js @@ -8,11 +8,7 @@ * MIT License */ -/* jshint -W004 */ -/* jshint -W030 */ -/* jshint -W031 */ -/* jshint -W083 */ -/* jshint -W108 */ +/* eslint-disable quotes, space-infix-ops, new-cap, keyword-spacing, no-redeclare, no-undef, no-new*/ (function() { @@ -2179,3 +2175,4 @@ global.Sound = Sound; } })(); +/* eslint-enable quotes, space-infix-ops, new-cap, keyword-spacing, no-redeclare, no-undef, no-new */ diff --git a/src/vendors/tween.js b/src/vendors/tween.js index 23ce6dcaa2..d3ff610c13 100644 --- a/src/vendors/tween.js +++ b/src/vendors/tween.js @@ -3,13 +3,7 @@ * https://github.com/sole/Tween.js */ -/* jshint -W011 */ -/* jshint -W013 */ -/* jshint -W089 */ -/* jshint -W093 */ -/* jshint -W098 */ -/* jshint -W108 */ -/* jshint -W116 */ +/* eslint-disable quotes, keyword-spacing, comma-spacing, no-return-assign */ (function() { @@ -886,3 +880,4 @@ } }); })(); +/* eslint-enable quotes, keyword-spacing, comma-spacing, no-return-assign */ diff --git a/src/video/canvas/texture.js b/src/video/canvas/texture.js index 333610a0f7..38b03b13e2 100644 --- a/src/video/canvas/texture.js +++ b/src/video/canvas/texture.js @@ -219,7 +219,7 @@ } // build the local atlas - for (var frame = 0, count = spritecount.x * spritecount.y; frame < count ; frame++) { + for (var frame = 0, count = spritecount.x * spritecount.y; frame < count; frame++) { atlas["" + frame] = { name: "" + frame, offset: new me.Vector2d( @@ -342,7 +342,7 @@ var region; // iterate through the given names // and create a "normalized" atlas - for (var i = 0; i < names.length;++i) { + for (var i = 0; i < names.length; ++i) { region = this.getRegion(names[i]); if (region == null) { // throw an error diff --git a/src/video/renderer.js b/src/video/renderer.js index 148c5a65aa..c661251a51 100644 --- a/src/video/renderer.js +++ b/src/video/renderer.js @@ -292,7 +292,7 @@ /** * @ignore */ - drawFont : function (/*bounds*/) {}, + drawFont : function (/*bounds*/) {} }); diff --git a/src/video/webgl/compositor.js b/src/video/webgl/compositor.js index 6a069fc054..ac3c7f714a 100644 --- a/src/video/webgl/compositor.js +++ b/src/video/webgl/compositor.js @@ -113,6 +113,7 @@ ).precision < 16) ? "mediump" : "highp"; // Load and create shader programs + /* eslint-disable */ this.lineShader = me.video.shader.createShader( this.gl, (__LINE_VERTEX__)(), @@ -128,6 +129,7 @@ "maxTextures" : this.maxTextures }) ); + /* eslint-enable */ this.shader = this.quadShader.handle; diff --git a/src/video/webgl/shader.js b/src/video/webgl/shader.js index d0a461e966..9f8b7b4e3d 100644 --- a/src/video/webgl/shader.js +++ b/src/video/webgl/shader.js @@ -55,7 +55,7 @@ "mat2" : "Matrix2fv", "mat3" : "Matrix3fv", "mat4" : "Matrix4fv", - "sampler2D" : "1i", + "sampler2D" : "1i" }; /** @@ -72,7 +72,7 @@ var program = { "attributes" : {}, "uniforms" : {}, - "handle" : null, + "handle" : null }, handle = program.handle = gl.createProgram(), attrRx = /attribute\s+\w+\s+(\w+)/g, @@ -149,7 +149,7 @@ gl[fnv](locations[name], val); }; } - })(name, type, "uniform" + fnHash[type]), + })(name, type, "uniform" + fnHash[type]) }; }); Object.defineProperties(program.uniforms, descriptor); diff --git a/src/video/webgl/webgl_renderer.js b/src/video/webgl/webgl_renderer.js index cabc49d3c4..96398aca4d 100644 --- a/src/video/webgl/webgl_renderer.js +++ b/src/video/webgl/webgl_renderer.js @@ -372,7 +372,7 @@ var attr = { antialias : this.antiAlias, - alpha : !opaque, + alpha : !opaque }; return ( c.getContext("webgl", attr) || diff --git a/tests/spec/bitmapfontdata-spec.js b/tests/spec/bitmapfontdata-spec.js index 5437bb21f5..d699a41ae2 100644 --- a/tests/spec/bitmapfontdata-spec.js +++ b/tests/spec/bitmapfontdata-spec.js @@ -164,4 +164,4 @@ describe("me.BitmapFontData", function () { expect(glyph.getKerning(44)).toEqual(-1); }); }); -}); \ No newline at end of file +}); diff --git a/tests/spec/draggable-spec.js b/tests/spec/draggable-spec.js index 54ab31f13d..affa53c751 100644 --- a/tests/spec/draggable-spec.js +++ b/tests/spec/draggable-spec.js @@ -6,6 +6,8 @@ * by simulating the system events that would have been triggered by pointer events */ +/* eslint-disable no-unused-vars, object-property-newline */ + (function (DraggableEntity, Event, Video) { "use strict"; describe("entity.draggable", function () { @@ -27,8 +29,7 @@ } }); // create a new draggable entity instance - draggable = new Draggable(position.x, position.y, {width: dimensions.x, height: - dimensions.y}); + draggable = new Draggable(position.x, position.y, {width: dimensions.x, height: dimensions.y}); // add the test draggable entity to the game me.game.world.addChild(draggable, 1); }, @@ -64,3 +65,4 @@ }); }); }(me.DraggableEntity, me.event, me.video)); +/* eslint-enable no-unused-vars, object-property-newline */ diff --git a/tests/spec/droptarget-spec.js b/tests/spec/droptarget-spec.js index e128e9393c..b444ee7c79 100644 --- a/tests/spec/droptarget-spec.js +++ b/tests/spec/droptarget-spec.js @@ -7,6 +7,8 @@ * pointer events */ +/* eslint-disable no-unused-vars, object-property-newline */ + (function (DraggableEntity, DroptargetEntity, Event, Video) { "use strict"; describe("entity.droptarget", function () { @@ -150,3 +152,4 @@ }); }); }(me.DraggableEntity, me.DroptargetEntity, me.event, me.video)); +/* eslint-enable no-unused-vars, object-property-newline */ diff --git a/tests/spec/es6-collections.js b/tests/spec/es6-collections.js index 0977417c7b..776dc7a0f9 100644 --- a/tests/spec/es6-collections.js +++ b/tests/spec/es6-collections.js @@ -21,8 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -describe("ES6 Collections test", function(){ +describe("ES6 Collections test", function() { it("WeakMap existence", function () { expect(WeakMap).toBeDefined(); }); @@ -32,7 +31,7 @@ describe("ES6 Collections test", function(){ var a = {}; var b = {}; var c = new WeakMap(); - var m = new WeakMap([[a,1], [b,2], [c, 3]]); + var m = new WeakMap([[a, 1], [b, 2], [c, 3]]); expect(m.has(a)).toEqual(true); expect(m.has(b)).toEqual(true); expect(m.has(c)).toEqual(true); @@ -106,7 +105,7 @@ describe("ES6 Collections test", function(){ var a = 1; var b = {}; var c = new Map(); - var m = new Map([[1,1], [b,2], [c, 3]]); + var m = new Map([[1, 1], [b, 2], [c, 3]]); expect(m.has(a)).toEqual(true); expect(m.has(b)).toEqual(true); expect(m.has(c)).toEqual(true); @@ -261,7 +260,7 @@ describe("ES6 Collections test", function(){ expect(JSON.stringify(o._values)).toEqual(JSON.stringify([1, 3, 0])); }); - it("Map#clear", function(){ + it("Map#clear", function() { var o = new Map(); o.set(1, "1"); o.set(2, "2"); diff --git a/tests/spec/es6-promise.js b/tests/spec/es6-promise.js index fb19ff9106..5bfc86b2c3 100644 --- a/tests/spec/es6-promise.js +++ b/tests/spec/es6-promise.js @@ -3,16 +3,16 @@ // https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve describe("ES6-Promises", function() { - it("Success", function(done) { + it("Success", function(done) { var foo = Promise.resolve("Success"); foo.then(function(value) { expect(value).toEqual("Success"); done(); }); - }); + }); it("Resolving an array", function(done) { - var foo = Promise.resolve([1,2,3]); + var foo = Promise.resolve([1, 2, 3]); foo.then(function(v) { expect(v[0]).toEqual(1); done(); diff --git a/tests/spec/inheritance-spec.js b/tests/spec/inheritance-spec.js index 247857375d..eb56e138cc 100644 --- a/tests/spec/inheritance-spec.js +++ b/tests/spec/inheritance-spec.js @@ -136,7 +136,7 @@ describe("Jay Inheritance", function () { }, "reset" : function () { this._data = []; - }, + } }; beforeEach(function () { @@ -188,7 +188,7 @@ describe("Jay Inheritance", function () { [ "B.foo" ], [ "D.bar" ], [ "C.bar" ], - [ "A.bar" ], + [ "A.bar" ] ]); }); }); @@ -231,7 +231,7 @@ describe("Jay Inheritance", function () { expect(stepper.steps()).toEqual([ [ "Z.foo" ], [ "Y.foo" ], - [ "Y.bar" ], + [ "Y.bar" ] ]); }); }); diff --git a/tests/spec/observableVect2d-spec.js b/tests/spec/observableVect2d-spec.js index 0c93a2c3bd..d44207ea0c 100644 --- a/tests/spec/observableVect2d-spec.js +++ b/tests/spec/observableVect2d-spec.js @@ -1,11 +1,11 @@ describe("me.ObservableVector2d", function () { var x = 1, y = 2; - + var a, b, c; - + var _newX, _newY, _oldX, _oldY; - + var callback = function (newX, newY, oldX, oldY) { // this will also validate the argument list _newX = newX; @@ -13,7 +13,7 @@ describe("me.ObservableVector2d", function () { _oldX = oldX; _oldY = oldY; }; - + it("should be initialized to a (0, 0) 2d vector", function () { a = new me.ObservableVector2d(0, 0, { onUpdate : callback.bind(this) @@ -37,41 +37,41 @@ describe("me.ObservableVector2d", function () { a.add(new me.Vector2d(10, 10)); expect(a.y).toEqual(_oldY + 10); }); - + it("sub a vector triggers the callback", function () { a.sub(new me.Vector2d(10, 10)); expect(a.x).toEqual(_oldX - 10); }); - + it("a(1, 2) should be copied into b", function () { a.set(x, y); b.copy(a); - + expect(b.equals(a)).toEqual(true); }); - - + + it("scale (1, 2) by (-1, -2)", function () { a.set(x, y); b.set(-x, -y); expect(a.scaleV(b).toString()).toEqual("x:"+x*(-x)+",y:"+y*(-y)); }); - + it("negate (1, 2)", function () { a.set(x, y); expect(a.negateSelf().toString()).toEqual("x:"+-x+",y:"+-y); }); - + it("dotProduct (1, 2) and (-1, -2)", function () { a.set(x, y); b.set(-x, -y); - + // calculate the dot product expect(a.dotProduct(b)).toEqual((-x*x-y*y)); }); - + it("length/lengthSqrt functions", function () { a.set( x, 0); b.set( 0, -y); @@ -88,7 +88,7 @@ describe("me.ObservableVector2d", function () { expect( a.length() ).toEqual(Math.sqrt( x*x + y*y )); expect( a.length2() ).toEqual(( x*x + y*y )); }); - + it("normalize function", function () { a.set( x, 0 ); b.set( 0, -y ); @@ -102,7 +102,7 @@ describe("me.ObservableVector2d", function () { expect( b.y ).toEqual(-1); }); - + it("distance function", function () { a.set( x, 0); b.set( 0, -y); @@ -111,7 +111,7 @@ describe("me.ObservableVector2d", function () { expect( a.distance( c ) ).toEqual(x); expect( b.distance( c ) ).toEqual(y); }); - + it( "min/max/clamp", function() { a.set( x, y ); b.set( -x, -y ); @@ -130,7 +130,7 @@ describe("me.ObservableVector2d", function () { expect( c.x ).toEqual(-x); expect( c.y ).toEqual(x); }); - + it( "ceil/floor", function() { expect(a.setMuted( -0.1, 0.1 ).floorSelf().equals(new me.Vector2d( -1, 0 ))).toEqual(true); expect(b.setMuted( -0.5, 0.5 ).floorSelf().equals(new me.Vector2d( -1, 0 ))).toEqual(true); @@ -140,33 +140,33 @@ describe("me.ObservableVector2d", function () { expect(b.setMuted( -0.5, 0.5 ).ceilSelf().equals(new me.Vector2d( 0, 1 ))).toEqual(true); expect(c.setMuted( -0.9, 0.9 ).ceilSelf().equals(new me.Vector2d( 0, 1 ))).toEqual(true); }); - + it("project a on b", function () { a.set(x, y); b.set(-x, -y); - + // the following only works with (-)1, (-)2style of values expect(a.project(b).equals(b)).toEqual(true); }); - + it("angle between a and b", function () { a.set(x, y); b.set(-x, -y); - + // why is this not perfectly 180 degrees ? expect(Math.round(a.angle(b).radToDeg()) ).toEqual(180); - + b.set(4*x, -y); expect(a.angle(b) ).toEqual(Math.PI / 2); }); - + it("perp and rotate function", function () { a.set(x, y); b.copy(a).perp(); // perp rotate the vector by 90 degree clockwise on the z axis c.copy(a).rotate(Math.PI/2); - + expect(a.angle(b)).toEqual(a.angle(c)); }); - -}); \ No newline at end of file + +}); diff --git a/tests/spec/observableVect3d-spec.js b/tests/spec/observableVect3d-spec.js index 9277eb799c..d115e6895b 100644 --- a/tests/spec/observableVect3d-spec.js +++ b/tests/spec/observableVect3d-spec.js @@ -1,11 +1,11 @@ describe("me.ObservableVector3d", function () { var x = 1, y = 2, z = 3; - + var a, b, c, d; - + var _newX, _newY, _newZ, _oldX, _oldY, _oldZ; - + var callback = function (newX, newY, newZ, oldX, oldY, oldZ) { // this will also validate the argument list _newX = newX; @@ -15,7 +15,7 @@ describe("me.ObservableVector3d", function () { _oldY = oldY; _oldZ = oldZ; }; - + it("should be initialized to a (0, 0, 0) 3d vector", function () { a = new me.ObservableVector3d(0, 0, 0, { onUpdate : callback.bind(this) @@ -23,57 +23,57 @@ describe("me.ObservableVector3d", function () { b = new me.ObservableVector3d(x, 0, 0, { onUpdate : callback.bind(this) }); - c = new me.ObservableVector3d(x, y, 0,{ + c = new me.ObservableVector3d(x, y, 0, { onUpdate : callback.bind(this) }); - - d = new me.ObservableVector3d(x, y, z,{ + + d = new me.ObservableVector3d(x, y, z, { onUpdate : callback.bind(this) }); - + expect(a.toString()).toEqual("x:0,y:0,z:0"); }); it("setting the vector triggers the callback", function () { a.set(10, 100, 20); - + expect(a.x + a.y + a.z).toEqual(_newX + _newY + _newZ); }); it("add a vector triggers the callback", function () { a.add(new me.Vector3d(10, 10, 10)); - + expect(a.y).toEqual(_oldY + 10); }); - + it("sub a vector triggers the callback", function () { a.sub(new me.Vector3d(10, 10, 10)); - + expect(a.x).toEqual(_oldX - 10); }); - + it("scale a vector triggers the callback", function () { a.scaleV(new me.Vector3d(10, 10, 10)); - + expect(a.x).toEqual(_oldX * 10); expect(a.y).toEqual(_oldY * 10); expect(a.z).toEqual(_oldZ * 10); }); - + it("negate (1, 2, 3)", function () { a.set(x, y, z); expect(a.negateSelf().toString()).toEqual("x:"+-x+",y:"+-y+",z:"+-z); }); - + it("dotProduct (1, 2, 3) and (-1, -2, -3)", function () { a.set(x, y, z); b.set(-x, -y, -z); - + // calculate the dot product expect(a.dotProduct(b)).toEqual(-x*x-y*y-z*z); }); - + it("length/lengthSqrt functions", function () { a.set( x, 0, 0 ); b.set( 0, -y, 0 ); @@ -90,11 +90,11 @@ describe("me.ObservableVector3d", function () { expect( d.length2() ).toEqual(0); a.set( x, y, z ); - + expect( a.length() ).toEqual(Math.sqrt( x*x + y*y + z*z )); expect( a.length2() ).toEqual(( x*x + y*y + z*z )); }); - + it("normalize function", function () { a.set( x, 0, 0 ); b.set( 0, -y, 0 ); @@ -112,7 +112,7 @@ describe("me.ObservableVector3d", function () { expect( c.length() ).toEqual(1); expect( c.z ).toEqual(1); }); - + it("distance function", function () { a.set( x, 0, 0 ); b.set( 0, -y, 0 ); @@ -123,11 +123,11 @@ describe("me.ObservableVector3d", function () { expect( b.distance( d ) ).toEqual(y); expect( c.distance( d ) ).toEqual(z); }); - + it( "min/max/clamp", function() { a.set( x, y, z ); b.set( -x, -y, -z ); - c.set(0 ,0, 0); + c.set( 0, 0, 0 ); c.copy( a ).minV( b ); expect( c.x ).toEqual(-x); @@ -145,7 +145,7 @@ describe("me.ObservableVector3d", function () { expect( c.y ).toEqual(x); expect( c.z ).toEqual(x); }); - + it( "ceil/floor", function() { expect( a.set( -0.1, 0.1, 0.3 ).floorSelf().equals(new me.Vector3d( -1, 0, 0 ))).toEqual(true); expect( a.set( -0.5, 0.5, 0.6 ).floorSelf().equals(new me.Vector3d( -1, 0, 0 ))).toEqual(true); @@ -159,36 +159,36 @@ describe("me.ObservableVector3d", function () { it("project a on b", function () { a.set(x, y, z); b.set(-x, -y, -z); - + // the following only works with (-)1, (-)2, (-)3 style of values expect(a.project(b).equals(b)).toEqual(true); }); - + it("angle between a and b", function () { - + a.set( 0, -0.18851655680720186, 0.9820700116639124 ); b.set( 0, 0.18851655680720186, -0.9820700116639124 ); expect( a.angle( a ) ).toEqual(0); expect( a.angle( b ) ).toEqual(Math.PI); - + a.set(x, y, 0); b.set(-x, -y, 0); - + // why is this not perfectly 180 degrees ? expect(Math.round(a.angle(b).radToDeg()) ).toEqual(180); - + b.set(4*x, -y, 0); expect(a.angle(b) ).toEqual(Math.PI / 2); }); - + it("perp and rotate function", function () { a.set(x, y, z); b.copy(a).perp(); // perp rotate the vector by 90 degree clockwise on the z axis c.copy(a).rotate(Math.PI/2); - + expect(a.angle(b)).toEqual(a.angle(c)); }); - -}); \ No newline at end of file + +}); diff --git a/tests/spec/vect2d-spec.js b/tests/spec/vect2d-spec.js index e75a5bbfc2..dbb4b47af8 100644 --- a/tests/spec/vect2d-spec.js +++ b/tests/spec/vect2d-spec.js @@ -1,7 +1,7 @@ describe("me.Vector2d", function () { var x = 1, y = 2; - + var a, b, c; it("should be initialized to a (0, 0) 2d vector", function () { @@ -15,51 +15,51 @@ describe("me.Vector2d", function () { it("a(1, 2) should be copied into b", function () { a.set(x, y); b.copy(a); - + expect(b.equals(a)).toEqual(true); }); - + it("set (1, 2) into a defined vector", function () { a.set(x, y); expect(a.toString()).toEqual("x:"+x+",y:"+y); }); - + it("add (1, 2) to (-1, -2)", function () { a.set(x, y); b.set(-x, -y); expect(a.add(b).toString()).toEqual("x:0,y:0"); }); - + it("sub (1, 2) to (-1, -2)", function () { a.set(x, y); b.set(-x, -y); expect(a.sub(b).toString()).toEqual("x:"+(x-(-x))+",y:"+(y-(-y))); }); - + it("scale (1, 2) by (-1, -2)", function () { a.set(x, y); b.set(-x, -y); expect(a.scaleV(b).toString()).toEqual("x:"+x*(-x)+",y:"+y*(-y)); }); - + it("negate (1, 2)", function () { a.set(x, y); expect(a.negateSelf().toString()).toEqual("x:"+-x+",y:"+-y); }); - + it("dotProduct (1, 2) and (-1, -2)", function () { a.set(x, y); b.set(-x, -y); - + // calculate the dot product expect(a.dotProduct(b)).toEqual((-x*x-y*y)); }); - + it("length/lengthSqrt functions", function () { a.set( x, 0); b.set( 0, -y); @@ -76,7 +76,7 @@ describe("me.Vector2d", function () { expect( a.length() ).toEqual(Math.sqrt( x*x + y*y )); expect( a.length2() ).toEqual(( x*x + y*y )); }); - + it("normalize function", function () { a.set( x, 0 ); b.set( 0, -y ); @@ -90,7 +90,7 @@ describe("me.Vector2d", function () { expect( b.y ).toEqual(-1); }); - + it("distance function", function () { a.set( x, 0); b.set( 0, -y); @@ -99,7 +99,7 @@ describe("me.Vector2d", function () { expect( a.distance( c ) ).toEqual(x); expect( b.distance( c ) ).toEqual(y); }); - + it( "min/max/clamp", function() { a.set( x, y ); b.set( -x, -y ); @@ -118,7 +118,7 @@ describe("me.Vector2d", function () { expect( c.x ).toEqual(-x); expect( c.y ).toEqual(x); }); - + it( "ceil/floor", function() { expect( new me.Vector2d( -0.1, 0.1 ).floorSelf().equals(new me.Vector2d( -1, 0 ))).toEqual(true); expect( new me.Vector2d( -0.5, 0.5 ).floorSelf().equals(new me.Vector2d( -1, 0 ))).toEqual(true); @@ -128,33 +128,33 @@ describe("me.Vector2d", function () { expect( new me.Vector2d( -0.5, 0.5 ).ceilSelf().equals(new me.Vector2d( 0, 1 ))).toEqual(true); expect( new me.Vector2d( -0.9, 0.9 ).ceilSelf().equals(new me.Vector2d( 0, 1 ))).toEqual(true); }); - + it("project a on b", function () { a.set(x, y); b.set(-x, -y); - + // the following only works with (-)1, (-)2style of values expect(a.project(b).equals(b)).toEqual(true); }); - + it("angle between a and b", function () { a.set(x, y); b.set(-x, -y); - + // why is this not perfectly 180 degrees ? expect(Math.round(a.angle(b).radToDeg()) ).toEqual(180); - + b.set(4*x, -y); expect(a.angle(b) ).toEqual(Math.PI / 2); }); - + it("perp and rotate function", function () { a.set(x, y); b.copy(a).perp(); // perp rotate the vector by 90 degree clockwise on the z axis c.copy(a).rotate(Math.PI/2); - + expect(a.angle(b)).toEqual(a.angle(c)); }); - + }); diff --git a/tests/spec/vect3d-spec.js b/tests/spec/vect3d-spec.js index d4ed881119..03eeb4b4ef 100644 --- a/tests/spec/vect3d-spec.js +++ b/tests/spec/vect3d-spec.js @@ -1,7 +1,7 @@ describe("me.Vector3d", function () { var x = 1, y = 2, z = 3; - + var a, b, c, d; it("should be initialized to a (0, 0, 0) 3d vector", function () { @@ -16,30 +16,30 @@ describe("me.Vector3d", function () { it("a(1, 2, 3) should be copied into b", function () { a.set(x, y, z); b.copy(a); - + expect(b.equals(a)).toEqual(true); }); - + it("set (1, 2, 3) into a defined vector", function () { a.set(x, y, z); expect(a.toString()).toEqual("x:"+x+",y:"+y+",z:"+z); }); - + it("add (1, 2, 3) to (-1, -2, -3)", function () { a.set(x, y, z); b.set(-x, -y, -z); expect(a.add(b).toString()).toEqual("x:0,y:0,z:0"); }); - + it("sub (1, 2, 3) to (-1, -2, -3)", function () { a.set(x, y, z); b.set(-x, -y, -z); expect(a.sub(b).toString()).toEqual("x:"+(x-(-x))+",y:"+(y-(-y))+",z:"+(z-(-z))); }); - + it("scale (1, 2, 3) by (-1, -2, -3)", function () { a.set(x, y, z); b.set(-x, -y, -z); @@ -47,24 +47,24 @@ describe("me.Vector3d", function () { expect(a.scaleV(b).toString()).toEqual("x:"+x*(-x)+",y:"+y*(-y)+",z:"+z*(-z)); a.set(x, y, z); - + expect(a.scale(-1).equals(b)).toEqual(true); }); - + it("negate (1, 2, 3)", function () { a.set(x, y, z); expect(a.negateSelf().toString()).toEqual("x:"+-x+",y:"+-y+",z:"+-z); }); - + it("dotProduct (1, 2, 3) and (-1, -2, -3)", function () { a.set(x, y, z); b.set(-x, -y, -z); - + // calculate the dot product expect(a.dotProduct(b)).toEqual(-x*x-y*y-z*z); }); - + it("length/lengthSqrt functions", function () { a.set( x, 0, 0 ); b.set( 0, -y, 0 ); @@ -81,11 +81,11 @@ describe("me.Vector3d", function () { expect( d.length2() ).toEqual(0); a.set( x, y, z ); - + expect( a.length() ).toEqual(Math.sqrt( x*x + y*y + z*z )); expect( a.length2() ).toEqual(( x*x + y*y + z*z )); }); - + it("normalize function", function () { a.set( x, 0, 0 ); b.set( 0, -y, 0 ); @@ -103,7 +103,7 @@ describe("me.Vector3d", function () { expect( c.length() ).toEqual(1); expect( c.z ).toEqual(1); }); - + it("distance function", function () { a.set( x, 0, 0 ); b.set( 0, -y, 0 ); @@ -114,11 +114,11 @@ describe("me.Vector3d", function () { expect( b.distance( d ) ).toEqual(y); expect( c.distance( d ) ).toEqual(z); }); - + it( "min/max/clamp", function() { a.set( x, y, z ); b.set( -x, -y, -z ); - c.set(0 ,0, 0); + c.set(0, 0, 0); c.copy( a ).minV( b ); expect( c.x ).toEqual(-x); @@ -136,7 +136,7 @@ describe("me.Vector3d", function () { expect( c.y ).toEqual(x); expect( c.z ).toEqual(x); }); - + it( "ceil/floor", function() { expect( a.set( -0.1, 0.1, 0.3 ).floorSelf().equals(new me.Vector3d( -1, 0, 0 ))).toEqual(true); expect( a.set( -0.5, 0.5, 0.6 ).floorSelf().equals(new me.Vector3d( -1, 0, 0 ))).toEqual(true); @@ -150,38 +150,38 @@ describe("me.Vector3d", function () { it("project a on b", function () { a.set(x, y, z); b.set(-x, -y, -z); - + // the following only works with (-)1, (-)2, (-)3 style of values expect(a.project(b).equals(b)).toEqual(true); }); - + it("angle between a and b", function () { - + a.set( 0, -0.18851655680720186, 0.9820700116639124 ); b.set( 0, 0.18851655680720186, -0.9820700116639124 ); expect( a.angle( a ) ).toEqual(0); expect( a.angle( b ) ).toEqual(Math.PI); - + a.set(x, y, 0); b.set(-x, -y, 0); - + // why is this not perfectly 180 degrees ? expect(Math.round(a.angle(b).radToDeg()) ).toEqual(180); - + b.set(4*x, -y, 0); expect(a.angle(b) ).toEqual(Math.PI / 2); }); - - + + it("perp and rotate function", function () { a.set(x, y, z); b.copy(a).perp(); // perp rotate the vector by 90 degree clockwise on the z axis c.copy(a).rotate(Math.PI/2); - + expect(a.angle(b)).toEqual(a.angle(c)); }); - - + + }); From a2bf1e2b858c920b1af413cdcf20b63d89a082be Mon Sep 17 00:00:00 2001 From: Olivier Biot Date: Tue, 8 Nov 2016 16:28:28 +0800 Subject: [PATCH 157/177] [#828] fix the travis build task looks like it does not like the strict mode : /home/travis/build/melonjs/melonJS/node_modules/grunt-eslint/tasks/eslint.js:2 const chalk = require('chalk'); ^^^^^ Loading "eslint.js" tasks...ERROR >> SyntaxError: Use of const in strict mode. Warning: Task "eslint:beforeConcat" not found. Use --force to continue. Aborted due to warnings. npm ERR! Test failed. See above for more details. --- .eslintrc.json | 1 - 1 file changed, 1 deletion(-) diff --git a/.eslintrc.json b/.eslintrc.json index 697f1da15d..bfc00ff400 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,6 +1,5 @@ { "env": { - "es6": true, "browser": true }, From c85b4a279ddeaad0a8d9d7574d24a7d29238b8e5 Mon Sep 17 00:00:00 2001 From: obiot Date: Tue, 8 Nov 2016 16:51:38 +0800 Subject: [PATCH 158/177] Revert "[#828] fix the travis build task" This reverts commit a2bf1e2b858c920b1af413cdcf20b63d89a082be. --- .eslintrc.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.eslintrc.json b/.eslintrc.json index bfc00ff400..697f1da15d 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,5 +1,6 @@ { "env": { + "es6": true, "browser": true }, From 5ecbced95be095c0642c72d3285b60bf0c7ffb20 Mon Sep 17 00:00:00 2001 From: obiot Date: Tue, 8 Nov 2016 16:51:46 +0800 Subject: [PATCH 159/177] other attempt at fixing the travis task --- Gruntfile.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 07b2109414..bc45a940f5 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,7 +1,5 @@ // eslint-disable-next-line module.exports = function (grunt) { - "use strict"; - var sourceFiles = grunt.file.readJSON("sourceFiles.json"); var testSpecs = grunt.file.readJSON("testSpecs.json"); From 5cb62c66a59dbde0f489827800417f79dc8d863f Mon Sep 17 00:00:00 2001 From: obiot Date: Tue, 8 Nov 2016 16:56:51 +0800 Subject: [PATCH 160/177] Revert "other attempt at fixing the travis task" This reverts commit 5ecbced95be095c0642c72d3285b60bf0c7ffb20. --- Gruntfile.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Gruntfile.js b/Gruntfile.js index bc45a940f5..07b2109414 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,5 +1,7 @@ // eslint-disable-next-line module.exports = function (grunt) { + "use strict"; + var sourceFiles = grunt.file.readJSON("sourceFiles.json"); var testSpecs = grunt.file.readJSON("testSpecs.json"); From e62d171f7dd1caa98e90b4b60df9ecb22abefae1 Mon Sep 17 00:00:00 2001 From: obiot Date: Tue, 8 Nov 2016 17:44:12 +0800 Subject: [PATCH 161/177] update the node.js version to fix the travis task --- .travis.yml | 3 +-- package.json | 2 +- scripts/travis-upload.sh | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index f1473a60cf..dc52ee4712 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,7 @@ sudo: false language: node_js node_js: - - 0.10 - - 0.12 + - 6 before_install: - "npm install -g grunt-cli" diff --git a/package.json b/package.json index 9fa704a54e..23dcc592e0 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "email": "contact@melonjs.org" }, "engines": { - "node": ">= 0.10.0" + "node": ">= 6" }, "main": "build/melonJS.js", "dependencies": {}, diff --git a/scripts/travis-upload.sh b/scripts/travis-upload.sh index f3e30de6da..1c3e752aed 100755 --- a/scripts/travis-upload.sh +++ b/scripts/travis-upload.sh @@ -1,7 +1,7 @@ #!/bin/bash set -ev -if [ "${TRAVIS_NODE_VERSION}" = "0.10" ] ; then +if [ "${TRAVIS_NODE_VERSION}" = "6" ] ; then artifacts upload \ --bucket melonjs-builds \ --s3-region us-east-1 \ From c94312daf5629b7fe447a45007843b57c50daae6 Mon Sep 17 00:00:00 2001 From: Jay Oster Date: Tue, 8 Nov 2016 17:57:58 -0800 Subject: [PATCH 162/177] Add Travis environment for node.js 7 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index dc52ee4712..477c4b9bfe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ language: node_js node_js: - 6 + - 7 before_install: - "npm install -g grunt-cli" From a37e002ee83b0bc53f52b6ef9cc41dbba5ccac22 Mon Sep 17 00:00:00 2001 From: obiot Date: Wed, 9 Nov 2016 11:02:33 +0800 Subject: [PATCH 163/177] version update for the basic grunt tasks and dependencies i did not touch so far uglify (we have some custom stuff in the config), jsdoc and jasmine core --- package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 23dcc592e0..10dca55ac0 100644 --- a/package.json +++ b/package.json @@ -24,16 +24,16 @@ "dot": "git+https://github.com/parasyte/doT.git#bug/replace-ext", "grunt": "^0.4.5", "grunt-build-gh-pages": "^1.0.5", - "grunt-contrib-clean": "^0.7.0", - "grunt-contrib-concat": "^0.5.1", - "grunt-contrib-connect": "^0.11.2", - "grunt-contrib-copy": "^0.8.2", - "grunt-contrib-jasmine": "^1.0.0", + "grunt-contrib-clean": "^1.0.0", + "grunt-contrib-concat": "^1.0.1", + "grunt-contrib-connect": "^1.0.2", + "grunt-contrib-copy": "^1.0.0", + "grunt-contrib-jasmine": "^1.0.3", "grunt-eslint": "^19.0.0", "grunt-contrib-uglify": "^1.0.1", "grunt-jsdoc": "^1.1.0", - "grunt-replace": "^0.11.0", - "grunt-shell": "^1.1.2", + "grunt-replace": "^1.0.1", + "grunt-shell": "^2.1.0", "jasmine-core": "2.4.1", "q": "^1.4.1", "shelljs": "^0.5.3" From b63cdf11e7b897e5f982a70a161088f7ef911e09 Mon Sep 17 00:00:00 2001 From: obiot Date: Thu, 10 Nov 2016 09:26:30 +0800 Subject: [PATCH 164/177] documentation fix --- src/level/TMXLayer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/level/TMXLayer.js b/src/level/TMXLayer.js index b5580be058..37553d8294 100644 --- a/src/level/TMXLayer.js +++ b/src/level/TMXLayer.js @@ -394,7 +394,7 @@ /** * All animated tilesets in this layer - * @private + * @ignore * @type Array * @name me.TMXLayer#animatedTilesets */ From b0cdc7aa7895086a89c88bf6d93d23001ed72c4b Mon Sep 17 00:00:00 2001 From: obiot Date: Thu, 10 Nov 2016 09:43:18 +0800 Subject: [PATCH 165/177] [doc] small example for the `getTile` function --- src/level/TMXLayer.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/level/TMXLayer.js b/src/level/TMXLayer.js index 37553d8294..e3c0fd5471 100644 --- a/src/level/TMXLayer.js +++ b/src/level/TMXLayer.js @@ -547,6 +547,11 @@ * @param {Number} x X coordinate (in world/pixels coordinates) * @param {Number} y Y coordinate (in world/pixels coordinates) * @return {me.Tile} Tile Object + * @example + * // get the TMX Map Layer called "Front layer" + * var layer = me.game.world.getChildByName("Front Layer")[0]; + * // get the tile object corresponding to the latest pointer position + * var tile = layer.getTile(me.input.pointer.pos.x, me.input.pointer.pos.y); */ getTile : function (x, y) { return this.layerData[~~this.renderer.pixelToTileX(x, y)][~~this.renderer.pixelToTileY(y, x)]; From e18f0946d4e95af16634249de3736aefe509c832 Mon Sep 17 00:00:00 2001 From: obiot Date: Thu, 10 Nov 2016 13:42:13 +0800 Subject: [PATCH 166/177] [#830] add a `forEach` function to Container object (thanks krojew for the suggestion) --- CHANGELOG | 1 + src/renderable/container.js | 36 ++++++++++++++++ tests/spec/container-spec.js | 82 +++++++++++++++++++++--------------- 3 files changed, 85 insertions(+), 34 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 7382a401e5..88060a1971 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,7 @@ Version History --------------- 4.0.0 * Audio : updated to the latest Howler 2.0.1 version +* Container : added a 'forEach' function that allows to iterate through all children * Core : largely improved and completed the matrix implementation and usage * Core : Object.assign polyfill update * Core : various improvements and fixes to jasmine test units. diff --git a/src/renderable/container.js b/src/renderable/container.js index 9b37d41f1a..bd06cfbd00 100644 --- a/src/renderable/container.js +++ b/src/renderable/container.js @@ -199,6 +199,42 @@ } }, + /** + * The forEach() method executes a provided function once per child element.
+ * callback is invoked with three arguments:
+ * - the element value
+ * - the element index
+ * - the array being traversed
+ * @name forEach + * @memberOf me.Container + * @function + * @param {Function} callback + * @param {Object} [thisArg] value to use as this(i.e reference Object) when executing callback. + * @example + * // iterate through all children of the root container + * me.game.world.forEach(function (child) { + * // do something with the child + * }); + */ + forEach : function (callback, thisArg) { + var context = this, i = 0; + + var len = this.children.length; + + if (typeof callback !== "function") { + throw new me.Container.Error(callback + " is not a function"); + } + + if (arguments.length > 1) { + context = thisArg; + } + + while (i < len) { + callback.call(context, this.children[i], i, this.children); + i++; + } + }, + /** * Swaps the position (z-index) of 2 children * @name swapChildren diff --git a/tests/spec/container-spec.js b/tests/spec/container-spec.js index 0d2800d61c..3ed810cc77 100644 --- a/tests/spec/container-spec.js +++ b/tests/spec/container-spec.js @@ -1,47 +1,61 @@ describe("me.Container", function () { var container; + beforeEach(function () { + container = new me.Container(0, 0, 100, 100); + container._root = true; + }); + describe("isAttachedToRoot", function () { - describe("game world", function () { - beforeEach(function () { - container = new me.Container(0, 0, 100, 100); - container._root = true; - }); + it("should return true", function () { + expect(container.isAttachedToRoot()).toEqual(true); + }); - it("should return true", function () { - expect(container.isAttachedToRoot()).toEqual(true); - }); + it("a new container should not be root", function () { + var secondContainer = new me.Container(); + expect(secondContainer.isAttachedToRoot()).toEqual(false); + }); - it("me.Container bounds return default assigned size", function () { - var bounds = container.getBounds(); - expect(bounds.pos.x).toEqual(0); - expect(bounds.pos.y).toEqual(0); - expect(bounds.width).toEqual(100); - expect(bounds.height).toEqual(100); - }); + it("a new container when attached to game world should find root", function () { + var secondContainer = new me.Container(); + container.addChild(secondContainer); + expect(secondContainer.isAttachedToRoot()).toEqual(true); + }); + }); - it("me.Container childbounds return the union of all child bounds", function () { - container.addChild(new me.Renderable(50, 50, 100, 100)); - container.addChild(new me.Renderable(100, 100, 100, 100)); - // update the child bounds - container.updateChildBounds(); - var bounds = container.childBounds; - expect(bounds.pos.x).toEqual(50); - expect(bounds.pos.y).toEqual(50); - expect(bounds.width).toEqual(150); - expect(bounds.height).toEqual(150); - }); + describe("Container bounds test", function () { + it("me.Container bounds return default assigned size", function () { + var bounds = container.getBounds(); + expect(bounds.pos.x).toEqual(0); + expect(bounds.pos.y).toEqual(0); + expect(bounds.width).toEqual(100); + expect(bounds.height).toEqual(100); + }); - it("a new container should not be root", function () { - var secondContainer = new me.Container(); - expect(secondContainer.isAttachedToRoot()).toEqual(false); - }); + it("me.Container childbounds return the union of all child bounds", function () { + container.addChild(new me.Renderable(50, 50, 100, 100)); + container.addChild(new me.Renderable(100, 100, 100, 100)); + // update the child bounds + container.updateChildBounds(); + var bounds = container.childBounds; + expect(bounds.pos.x).toEqual(50); + expect(bounds.pos.y).toEqual(50); + expect(bounds.width).toEqual(150); + expect(bounds.height).toEqual(150); + }); + }); - it("a new container when attached to game world should find root", function () { - var secondContainer = new me.Container(); - container.addChild(secondContainer); - expect(secondContainer.isAttachedToRoot()).toEqual(true); + describe("Container utility function", function () { + var counter = 0; + it("forEach iterate through all children", function () { + container.addChild(new me.Renderable(50, 50, 100, 100)); + container.addChild(new me.Renderable(100, 100, 100, 100)); + container.forEach(function(child) { + if (child.ancestor === container) { + counter++; + } }); + expect(counter).toEqual(2); }); }); }); From e9b02f50ed71dfc510502b450b01007dde12e219 Mon Sep 17 00:00:00 2001 From: Olivier Biot Date: Sun, 27 Nov 2016 16:43:36 +0800 Subject: [PATCH 167/177] fix build dependencies on OSX --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 10dca55ac0..5ef7936da2 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "grunt-jsdoc": "^1.1.0", "grunt-replace": "^1.0.1", "grunt-shell": "^2.1.0", - "jasmine-core": "2.4.1", + "jasmine-core": "~2.5.2", "q": "^1.4.1", "shelljs": "^0.5.3" }, From a8172f78327255a1a96039cd190419398078d4a1 Mon Sep 17 00:00:00 2001 From: Olivier Biot Date: Sun, 27 Nov 2016 16:44:49 +0800 Subject: [PATCH 168/177] [#637] add a `debug` flag to the WebGL renderer Default to `off`. Once enabled, it will output warning messages in the console to log any mi-usage of the renderer --- src/video/video.js | 3 ++- src/video/webgl/compositor.js | 4 +++- src/video/webgl/webgl_renderer.js | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/video/video.js b/src/video/video.js index 38c1dfd16e..b7543eae61 100644 --- a/src/video/video.js +++ b/src/video/video.js @@ -38,7 +38,8 @@ scaleMethod : "fit", transparent : false, antiAlias : false, - subPixel : false + subPixel : false, + debug : false }; diff --git a/src/video/webgl/compositor.js b/src/video/webgl/compositor.js index ac3c7f714a..68bc34e0d8 100644 --- a/src/video/webgl/compositor.js +++ b/src/video/webgl/compositor.js @@ -383,7 +383,9 @@ var region = texture.getRegion(key); if (typeof(region) === "undefined") { // TODO: Require proper atlas regions instead of caching arbitrary region keys - console.warn("Adding texture region", key, "for texture", texture); + if (me.video.renderer.debug === true) { + console.warn("Adding texture region", key, "for texture", texture); + } var keys = key.split(","), sx = +keys[0], diff --git a/src/video/webgl/webgl_renderer.js b/src/video/webgl/webgl_renderer.js index 96398aca4d..b1698187b2 100644 --- a/src/video/webgl/webgl_renderer.js +++ b/src/video/webgl/webgl_renderer.js @@ -22,6 +22,7 @@ * @param {Boolean} [options.subPixel=false] Whether to enable subpixel renderering (performance hit when enabled) * @param {Number} [options.zoomX=width] The actual width of the canvas with scaling applied * @param {Number} [options.zoomY=height] The actual height of the canvas with scaling applied + * @param {Boolean} [options.debug=false] Enable the WebGL renderer debug mode (e.g. display warning messages about improrer usage) * @param {me.WebGLRenderer.Compositor} [options.compositor] A class that implements the compositor API */ me.WebGLRenderer = me.Renderer.extend( @@ -83,6 +84,8 @@ // Configure the WebGL viewport this.scaleCanvas(1, 1); + this.debug = !!(options.debug); + return this; }, From dc1aab3583c9649c50c6a1c361449f4d6da38305 Mon Sep 17 00:00:00 2001 From: Olivier Biot Date: Sun, 27 Nov 2016 18:33:29 +0800 Subject: [PATCH 169/177] [#840][#593] Fix the debugPanel in WebGL mode it now uses an embedded bitmapFont, that also fix the performance issue with WebGL --- plugins/debug/debugPanel.js | 194 ++++++++++++++++++++++------ plugins/debug/font/PressStart2P.fnt | 100 ++++++++++++++ plugins/debug/font/PressStart2P.ltr | 1 + plugins/debug/font/PressStart2P.png | Bin 0 -> 7672 bytes 4 files changed, 258 insertions(+), 37 deletions(-) create mode 100755 plugins/debug/font/PressStart2P.fnt create mode 100644 plugins/debug/font/PressStart2P.ltr create mode 100755 plugins/debug/font/PressStart2P.png diff --git a/plugins/debug/debugPanel.js b/plugins/debug/debugPanel.js index d6f5e408c4..fee6783538 100644 --- a/plugins/debug/debugPanel.js +++ b/plugins/debug/debugPanel.js @@ -43,6 +43,111 @@ } }); + // embedded bitmap font data + var fontDataSource = + "info face=\"PressStart2P\" size=10 bold=0 italic=0 charset= unicode= stretchH=100 smooth=1 aa=1 padding=1,1,1,1 spacing=0,0 outline=0\n" + + "common lineHeight=10 base=10 scaleW=128 scaleH=128 pages=1 packed=0\n" + + "page id=0 file=\"PressStart2P.png\"\n" + + "chars count=95\n" + + "char id=32 x=1 y=1 width=0 height=0 xoffset=0 yoffset=10 xadvance=10 page=0 chnl=15\n" + + "char id=33 x=1 y=2 width=5 height=10 xoffset=3 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=34 x=1 y=13 width=8 height=5 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=35 x=7 y=1 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=36 x=1 y=19 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=37 x=1 y=30 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=38 x=1 y=41 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=39 x=10 y=12 width=4 height=5 xoffset=3 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=40 x=1 y=52 width=6 height=10 xoffset=3 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=41 x=1 y=63 width=6 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=42 x=1 y=74 width=10 height=8 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15\n" + + "char id=43 x=8 y=52 width=9 height=8 xoffset=1 yoffset=1 xadvance=10 page=0 chnl=15\n" + + "char id=44 x=8 y=61 width=5 height=5 xoffset=1 yoffset=6 xadvance=10 page=0 chnl=15\n" + + "char id=45 x=8 y=67 width=9 height=2 xoffset=1 yoffset=4 xadvance=10 page=0 chnl=15\n" + + "char id=46 x=14 y=61 width=4 height=4 xoffset=3 yoffset=6 xadvance=10 page=0 chnl=15\n" + + "char id=47 x=12 y=18 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=48 x=18 y=1 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=49 x=12 y=29 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=50 x=12 y=40 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=51 x=22 y=29 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=52 x=23 y=12 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=53 x=29 y=1 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=54 x=1 y=83 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=55 x=1 y=94 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=56 x=1 y=105 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=57 x=1 y=116 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=58 x=18 y=51 width=4 height=8 xoffset=3 yoffset=1 xadvance=10 page=0 chnl=15\n" + + "char id=59 x=12 y=70 width=5 height=9 xoffset=1 yoffset=1 xadvance=10 page=0 chnl=15\n" + + "char id=60 x=12 y=80 width=8 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=61 x=23 y=23 width=10 height=5 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" + + "char id=62 x=18 y=66 width=8 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=63 x=23 y=40 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=64 x=33 y=29 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=65 x=23 y=51 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=66 x=34 y=12 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=67 x=40 y=1 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=68 x=12 y=91 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=69 x=21 y=77 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=70 x=27 y=62 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=71 x=34 y=40 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=72 x=34 y=51 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=73 x=44 y=23 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=74 x=45 y=12 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=75 x=51 y=1 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=76 x=12 y=102 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=77 x=12 y=113 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=78 x=22 y=102 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=79 x=23 y=88 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=80 x=32 y=73 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=81 x=38 y=62 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=82 x=23 y=113 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=83 x=33 y=99 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=84 x=34 y=84 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=85 x=43 y=73 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=86 x=34 y=110 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=87 x=44 y=84 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=88 x=44 y=95 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=89 x=45 y=106 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=90 x=45 y=117 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=91 x=45 y=34 width=6 height=10 xoffset=3 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=92 x=45 y=45 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=93 x=52 y=34 width=6 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=94 x=34 y=23 width=8 height=4 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=95 x=34 y=121 width=10 height=2 xoffset=0 yoffset=9 xadvance=10 page=0 chnl=15\n" + + "char id=96 x=15 y=12 width=4 height=4 xoffset=4 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=97 x=54 y=23 width=10 height=7 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" + + "char id=98 x=56 y=12 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=99 x=62 y=1 width=10 height=7 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" + + "char id=100 x=49 y=56 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=101 x=56 y=45 width=10 height=8 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" + + "char id=102 x=59 y=31 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=103 x=54 y=67 width=10 height=9 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" + + "char id=104 x=60 y=54 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=105 x=67 y=42 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=106 x=67 y=9 width=8 height=11 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=107 x=69 y=21 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=108 x=76 y=1 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=109 x=76 y=12 width=10 height=8 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" + + "char id=110 x=69 y=32 width=10 height=8 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" + + "char id=111 x=86 y=1 width=10 height=8 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" + + "char id=112 x=97 y=1 width=10 height=9 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" + + "char id=113 x=108 y=1 width=10 height=9 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" + + "char id=114 x=87 y=10 width=9 height=8 xoffset=1 yoffset=3 xadvance=10 page=0 chnl=15\n" + + "char id=115 x=97 y=11 width=10 height=7 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" + + "char id=116 x=108 y=11 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=117 x=87 y=19 width=10 height=7 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" + + "char id=118 x=98 y=19 width=9 height=8 xoffset=1 yoffset=3 xadvance=10 page=0 chnl=15\n" + + "char id=119 x=80 y=27 width=10 height=7 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" + + "char id=120 x=108 y=22 width=10 height=8 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" + + "char id=121 x=91 y=28 width=10 height=9 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" + + "char id=122 x=80 y=35 width=10 height=7 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" + + "char id=123 x=118 y=11 width=6 height=10 xoffset=3 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=124 x=102 y=28 width=4 height=10 xoffset=4 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=125 x=119 y=22 width=6 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15\n" + + "char id=126 x=91 y=38 width=10 height=5 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15\n" + + "char id=32 x=0 y=0 width=0 height=0 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15"; + + var fontImageSource = "data:image/png;base64,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"; + var DebugPanel = me.Renderable.extend({ /** @private */ init : function (showKey, hideKey) { @@ -50,7 +155,7 @@ this._super(me.Renderable, "init", [ 0, 0, me.game.viewport.width, DEBUG_HEIGHT ]); // minimum melonJS version expected - this.version = "2.2.0"; + this.version = "4.0.0"; // to hold the debug options // clickable rect area @@ -101,24 +206,36 @@ // create a default font, with fixed char width this.font_size = 10; - this.mod = 1; + this.mod = 2; if (this.width < 500) { this.font_size = 7; - this.mod = 0.7; + this.mod = this.mod * (this.font_size / 10); } - this.font = new me.Font("courier", this.font_size, "white"); + + // create the bitmapfont + var fontImage = new Image(); + fontImage.src = fontImageSource; + + this.font = new me.BitmapFont( + fontDataSource, + fontImage + ); + + // free static ressources + fontImageSource = null; + fontDataSource = null; // clickable areas - var size = 12 * this.mod; - this.area.renderHitBox = new me.Rect(163, 5, size, size); - this.area.renderVelocity = new me.Rect(163, 20, size, size); - this.area.renderQuadTree = new me.Rect(253, 5, size, size); + var size = 10 * this.mod; + this.area.renderHitBox = new me.Rect(250, 2, size, size); + this.area.renderVelocity = new me.Rect(250, 17, size, size); + this.area.renderQuadTree = new me.Rect(410, 2, size, size); // some internal string/length this.help_str = "(s)how/(h)ide"; this.help_str_len = this.font.measureText(me.video.renderer, this.help_str).width; this.fps_str_len = this.font.measureText(me.video.renderer, "00/00 fps").width; - this.memoryPositionX = 400 * this.mod; + this.memoryPositionX = 325 * this.mod; // enable the FPS counter me.debug.displayFPS = true; @@ -456,10 +573,10 @@ renderer.setColor("#3AA4F0"); renderer.fillRect(this.memoryPositionX + 1, 1, len - 1, 17); - this.font.draw(renderer, "Heap : " + usedHeap + "/" + totalHeap + " MB", this.memoryPositionX + 5, 5 * this.mod); + this.font.draw(renderer, "Heap : " + usedHeap + "/" + totalHeap + " MB", this.memoryPositionX + 5, 2 * this.mod); } else { // Heap Memory information not available - this.font.draw(renderer, "Heap : ??/?? MB", this.memoryPositionX, 5 * this.mod); + this.font.draw(renderer, "Heap : ??/?? MB", this.memoryPositionX, 2 * this.mod); } }, @@ -473,59 +590,62 @@ } // draw the panel - renderer.setGlobalAlpha(0.5); - renderer.setColor("black"); + renderer.setColor("#00000080"); renderer.fillRect( this.left, this.top, this.width, this.height ); - renderer.setGlobalAlpha(1.0); - this.font.draw(renderer, "#objects : " + me.game.world.children.length, 5 * this.mod, 5 * this.mod); - this.font.draw(renderer, "#draws : " + me.game.world.drawCount, 5 * this.mod, 20 * this.mod); + renderer.setColor("#FFFFFFFF"); + + this.font.textAlign = "left"; + + this.font.draw(renderer, "#objects : " + me.game.world.children.length, 5 * this.mod, 2 * this.mod); + this.font.draw(renderer, "#draws : " + me.game.world.drawCount, 5 * this.mod, 10 * this.mod); // debug checkboxes - this.font.draw(renderer, "?hitbox [" + (me.debug.renderHitBox ? "x" : " ") + "]", 100 * this.mod, 5 * this.mod); - this.font.draw(renderer, "?velocity [" + (me.debug.renderVelocity ? "x" : " ") + "]", 100 * this.mod, 20 * this.mod); + this.font.draw(renderer, "?hitbox [" + (me.debug.renderHitBox ? "x" : " ") + "]", 75 * this.mod, 2 * this.mod); + this.font.draw(renderer, "?velocity [" + (me.debug.renderVelocity ? "x" : " ") + "]", 75 * this.mod, 10 * this.mod); - this.font.draw(renderer, "?QuadTree [" + (me.debug.renderQuadTree ? "x" : " ") + "]", 190 * this.mod, 5 * this.mod); + this.font.draw(renderer, "?QuadTree [" + (me.debug.renderQuadTree ? "x" : " ") + "]", 150 * this.mod, 2 * this.mod); // draw the update duration - this.font.draw(renderer, "Update : " + this.frameUpdateTime.toFixed(2) + " ms", 285 * this.mod, 5 * this.mod); + this.font.draw(renderer, "Update : " + this.frameUpdateTime.toFixed(2) + " ms", 225 * this.mod, 2 * this.mod); // draw the draw duration - this.font.draw(renderer, "Draw : " + this.frameDrawTime.toFixed(2) + " ms", 285 * this.mod, 20 * this.mod); + this.font.draw(renderer, "Draw : " + this.frameDrawTime.toFixed(2) + " ms", 225 * this.mod, 10 * this.mod); - this.font.bold(); - // Draw color code hints - this.font.fillStyle.copy("red"); - this.font.draw(renderer, "Shapes : " + this.counters.get("shapes"), 5 * this.mod, 35 * this.mod); + // Draw color code hints (not supported with bitmapfont) + //this.font.fillStyle.copy("red"); + this.font.draw(renderer, "Shapes : " + this.counters.get("shapes"), 5 * this.mod, 17 * this.mod); - this.font.fillStyle.copy("green"); - this.font.draw(renderer, "Sprites : " + this.counters.get("sprites"), 100 * this.mod, 35 * this.mod); + //this.font.fillStyle.copy("green"); + this.font.draw(renderer, "Sprites : " + this.counters.get("sprites"), 75 * this.mod, 17 * this.mod); - this.font.fillStyle.copy("blue"); - this.font.draw(renderer, "Velocity : " + this.counters.get("velocity"), 190 * this.mod, 35 * this.mod); + //this.font.fillStyle.copy("blue"); + this.font.draw(renderer, "Velocity : " + this.counters.get("velocity"), 150 * this.mod, 17 * this.mod); - this.font.fillStyle.copy("orange"); - this.font.draw(renderer, "Bounds : " + this.counters.get("bounds"), 285 * this.mod, 35 * this.mod); + //this.font.fillStyle.copy("orange"); + this.font.draw(renderer, "Bounds : " + this.counters.get("bounds"), 225 * this.mod, 17 * this.mod); - this.font.fillStyle.copy("purple"); - this.font.draw(renderer, "Children : " + this.counters.get("children"), 400 * this.mod, 35 * this.mod); + //this.font.fillStyle.copy("purple"); + this.font.draw(renderer, "Children : " + this.counters.get("children"), 325 * this.mod, 17 * this.mod); // Reset font style - this.font.setFont("courier", this.font_size, "white"); + //this.font.setFont("courier", this.font_size, "white"); // draw the memory heap usage - var endX = this.width - 25; + var endX = this.width - 5; this.drawMemoryGraph(renderer, endX - this.help_str_len); + this.font.textAlign = "right"; + // some help string - this.font.draw(renderer, this.help_str, endX - this.help_str_len, 20 * this.mod); + this.font.draw(renderer, this.help_str, endX, 17 * this.mod); //fps counter var fps_str = me.timer.fps + "/" + me.sys.fps + " fps"; - this.font.draw(renderer, fps_str, this.width - this.fps_str_len - 5, 5 * this.mod); + this.font.draw(renderer, fps_str, endX, 2 * this.mod); renderer.restore(); }, diff --git a/plugins/debug/font/PressStart2P.fnt b/plugins/debug/font/PressStart2P.fnt new file mode 100755 index 0000000000..2b7a5d2645 --- /dev/null +++ b/plugins/debug/font/PressStart2P.fnt @@ -0,0 +1,100 @@ +info face=PressStart2P size=10 bold=0 italic=0 charset= unicode= stretchH=100 smooth=1 aa=1 padding=1,1,1,1 spacing=0,0 outline=0 +common lineHeight=10 base=10 scaleW=128 scaleH=128 pages=1 packed=0 +page id=0 file="PressStart2P.png" +chars count=95 +char id=32 x=1 y=1 width=0 height=0 xoffset=0 yoffset=10 xadvance=10 page=0 chnl=15 +char id=33 x=1 y=2 width=5 height=10 xoffset=3 yoffset=0 xadvance=10 page=0 chnl=15 +char id=34 x=1 y=13 width=8 height=5 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15 +char id=35 x=7 y=1 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=36 x=1 y=19 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=37 x=1 y=30 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=38 x=1 y=41 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=39 x=10 y=12 width=4 height=5 xoffset=3 yoffset=0 xadvance=10 page=0 chnl=15 +char id=40 x=1 y=52 width=6 height=10 xoffset=3 yoffset=0 xadvance=10 page=0 chnl=15 +char id=41 x=1 y=63 width=6 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15 +char id=42 x=1 y=74 width=10 height=8 xoffset=0 yoffset=1 xadvance=10 page=0 chnl=15 +char id=43 x=8 y=52 width=9 height=8 xoffset=1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=44 x=8 y=61 width=5 height=5 xoffset=1 yoffset=6 xadvance=10 page=0 chnl=15 +char id=45 x=8 y=67 width=9 height=2 xoffset=1 yoffset=4 xadvance=10 page=0 chnl=15 +char id=46 x=14 y=61 width=4 height=4 xoffset=3 yoffset=6 xadvance=10 page=0 chnl=15 +char id=47 x=12 y=18 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=48 x=18 y=1 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=49 x=12 y=29 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15 +char id=50 x=12 y=40 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=51 x=22 y=29 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=52 x=23 y=12 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=53 x=29 y=1 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=54 x=1 y=83 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=55 x=1 y=94 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=56 x=1 y=105 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=57 x=1 y=116 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=58 x=18 y=51 width=4 height=8 xoffset=3 yoffset=1 xadvance=10 page=0 chnl=15 +char id=59 x=12 y=70 width=5 height=9 xoffset=1 yoffset=1 xadvance=10 page=0 chnl=15 +char id=60 x=12 y=80 width=8 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15 +char id=61 x=23 y=23 width=10 height=5 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15 +char id=62 x=18 y=66 width=8 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15 +char id=63 x=23 y=40 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=64 x=33 y=29 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=65 x=23 y=51 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=66 x=34 y=12 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=67 x=40 y=1 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=68 x=12 y=91 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=69 x=21 y=77 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=70 x=27 y=62 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=71 x=34 y=40 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=72 x=34 y=51 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=73 x=44 y=23 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15 +char id=74 x=45 y=12 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=75 x=51 y=1 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=76 x=12 y=102 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15 +char id=77 x=12 y=113 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=78 x=22 y=102 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=79 x=23 y=88 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=80 x=32 y=73 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=81 x=38 y=62 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=82 x=23 y=113 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=83 x=33 y=99 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=84 x=34 y=84 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15 +char id=85 x=43 y=73 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=86 x=34 y=110 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=87 x=44 y=84 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=88 x=44 y=95 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=89 x=45 y=106 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15 +char id=90 x=45 y=117 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=91 x=45 y=34 width=6 height=10 xoffset=3 yoffset=0 xadvance=10 page=0 chnl=15 +char id=92 x=45 y=45 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=93 x=52 y=34 width=6 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15 +char id=94 x=34 y=23 width=8 height=4 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15 +char id=95 x=34 y=121 width=10 height=2 xoffset=0 yoffset=9 xadvance=10 page=0 chnl=15 +char id=96 x=15 y=12 width=4 height=4 xoffset=4 yoffset=0 xadvance=10 page=0 chnl=15 +char id=97 x=54 y=23 width=10 height=7 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15 +char id=98 x=56 y=12 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=99 x=62 y=1 width=10 height=7 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15 +char id=100 x=49 y=56 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=101 x=56 y=45 width=10 height=8 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15 +char id=102 x=59 y=31 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15 +char id=103 x=54 y=67 width=10 height=9 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15 +char id=104 x=60 y=54 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=105 x=67 y=42 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15 +char id=106 x=67 y=9 width=8 height=11 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15 +char id=107 x=69 y=21 width=10 height=10 xoffset=0 yoffset=0 xadvance=10 page=0 chnl=15 +char id=108 x=76 y=1 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15 +char id=109 x=76 y=12 width=10 height=8 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15 +char id=110 x=69 y=32 width=10 height=8 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15 +char id=111 x=86 y=1 width=10 height=8 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15 +char id=112 x=97 y=1 width=10 height=9 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15 +char id=113 x=108 y=1 width=10 height=9 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15 +char id=114 x=87 y=10 width=9 height=8 xoffset=1 yoffset=3 xadvance=10 page=0 chnl=15 +char id=115 x=97 y=11 width=10 height=7 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15 +char id=116 x=108 y=11 width=9 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15 +char id=117 x=87 y=19 width=10 height=7 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15 +char id=118 x=98 y=19 width=9 height=8 xoffset=1 yoffset=3 xadvance=10 page=0 chnl=15 +char id=119 x=80 y=27 width=10 height=7 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15 +char id=120 x=108 y=22 width=10 height=8 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15 +char id=121 x=91 y=28 width=10 height=9 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15 +char id=122 x=80 y=35 width=10 height=7 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15 +char id=123 x=118 y=11 width=6 height=10 xoffset=3 yoffset=0 xadvance=10 page=0 chnl=15 +char id=124 x=102 y=28 width=4 height=10 xoffset=4 yoffset=0 xadvance=10 page=0 chnl=15 +char id=125 x=119 y=22 width=6 height=10 xoffset=1 yoffset=0 xadvance=10 page=0 chnl=15 +char id=126 x=91 y=38 width=10 height=5 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15 +char id=32 x=0 y=0 width=0 height=0 xoffset=0 yoffset=3 xadvance=10 page=0 chnl=15 diff --git a/plugins/debug/font/PressStart2P.ltr b/plugins/debug/font/PressStart2P.ltr new file mode 100644 index 0000000000..d352d2852c --- /dev/null +++ b/plugins/debug/font/PressStart2P.ltr @@ -0,0 +1 @@ +{"font":{"size":10,"data":"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OwEdATsBBTUrAR0BKQE9ASsBgAEAAQBAQEBAgIBAQP7A/sBAQEBAQEBAQAIAwMBAQICA/wCAgAEAAQCAgAPAQEBAgIBAQICAQEBAQICAQECAgICAQEBAQMBAgIBAQAAAAAIAAACAA4AEAAAnAC8AABM1KQEdATsBGQErAR0BKwEdASkBPQE7AT0BOwE9ASkBPQErAT0BOwEFNSsBHQE7AYABQAFAQEBAQEBA/wD/AMDAQED/AP8AQEBAQAIAwMDAwAPAQEBA/wD/AEBAQEBAQEBAQEBAQICAgICAgAACAQABAAIAA4AABwAPAAABNTsBHQErARE1OwEdASsBAQCAgICAgICAgAGAgICAAgCAgIAAAgCAAIACAAOAAA8AFwAAATU7AR0BKwEdASsBPQE7ARE1OwEdASsBAQCAgEBAgIBAQICAgIABgICAgEBAQEACAICAgAABAIAAgAMABAAANwAAATU7AR0BKwEdASsBHQErAR0BOwEdATsBHQE7AR0BKwE9ASsBPQErAT0BKwE9ATsBPQE7AT0BOwECAICAQEBAQEBAQEBAQEBAgIBAQEBAQEBAQEBAQEADwEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAACAAABgAOAAwAABwAPAAARNSkBHQEpARE1KQEdASkBAcABwP5A/kABwAHA/kD+QAHAQEBAAUBAQEAAAAABAIAAgAMABAAANwAAEzU7AR0BOwEdATsBHQE7AR0BKwEdASsBHQErAR0BKwE9ATsBPQE7AT0BOwE9ASsBPQErAT0BKwGAgIBAQEBAQEBAQEBAQECAgEBAQEBAQEBAQEBAQAPAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAIAAACAA4AEAAAHAC8AACU1OwEdASsBAzUpAR0BOwEdASsBHQErAR0BKwE9ATsBPQE7AT0BKwEdASsBPQE7AQEAwMDAwIABQAFAQEBAQEBAwMCAgEBAwMCAgEBAwEBAQANAQEBAgIBAQEBAQEBAQEBAQECAgAAAAAIAAACAA4AEAAAnAC8AABM1KQEdATsBGQEpAT0BOwEdATsBPQEpARkBKQEdASkBPQErARkBOwEBNSsBHQE7AYABQAFAQED+wP7AwMBAQP7A/sABQAFA/sD+wEBAQEABgEBAQEADwEBAQP8A/wDAwICAwMD+wP7AQEBAQAFAAUD+wEBAQAAAAAACAAAAgAOABAAAHwAvAAABNTsBHQE7AR0BOwEZASsBPQErAR0BKwEZATsBPQE7AQU1KwEdASsBHQE7AT0BKwEBAMDAQEBAQICAwMCAgEBAQEABAEBAQEDAwEBAA8BAQEBAQP7A/sCAgICAAUABQEBAQEBAQICAgIAAAAAAAwAAAIADgAQAABcAHwAnAAAZASkBHQE7AR0BKwEdATsBHQErAR0BKQEBNSsBHQE7ARE1KwEdATsBAYABgEBAQEBAQEBA/oD+gAKAwMDAwMDAwMACQAHAQECAgEBAgIBAQAKAgICA/wCAgIAAAQAAAIADgAQAADcAAAE1KQEdATsBHQErAT0BKwEdASsBHQE7AR0BOwE9ATsBHQErAR0BKQE9ASsBPQErAT0BOwE9ATsBAQABAAEAQECAgICAQEBAQICAgIBAQP8A/wBAQEBAQEBAQAPAQEBAQEBAQEBAwMBAQEBAQEBAQEBAQEDAwEBAAAAAAAIAAACAA4AEAAAXACcAABkBKQEdATsBHQE7AR0BKwEdASsBHQEpAQE1KwEZATsBPQE7AT0BKwEBQAFAQEBAQEBAQED+wP7AAgCAgICAQEBAQAJAAcBAQEBAwMBAQEBAAsBA/sD+wEBAwMAAAAEAAACAA4AEAAAXAAAZASkBHQEpAR0BKQEdASkBHQEpAR0BKQEBwAHA/sD+wAEAAQD/AP8AAUABQP5A/kACQAHAQECAgEBAgIBAQAABAAAAgAOABAAAEwAAGQEpAR0BKQEdASkBHQEpAR0BKwEBwAHA/sD+wAEAAQD/AP8AgIACQAHAQECAgEBAwMAAAQAAAIADgAQAAC8AAAE1KQEdASkBHQErAR0BOwEdATsBPQErAT0BOwEZASkBPQErAT0BKwE9ATsBPQE7AQEAAUABQP8A/wBAQEBAgIBAQMDA/sD+wEBAQEBAQEBAA8BAQEBAQMDAQECAgEBA/wD/AEBAQEDAwEBAAAAAAAEAAACAA4AEAAAXAAAZATsBHQE7AT0BOwEZASsBPQErAR0BKwGAgMDAgICAgMDAgIACQAHAwMDAwP5A/kDAwMDAAAAAAQCAAIADgAQAABcAABM1KQEdASsBGQE7AR0BKQE9ATsBGQErAYABgAGAgICAgP6A/oCAgICAA8BAQED+wP7AQEBAQAFAAUAAAQAAAIADgAQAABcAAAEROwEZASsBHQEpAT0BKwE9ATsBHQE7AQKAgIBAQP7A/sBAQICAwMACgAGA/oD+gEBAQEBAQEBAAAAAAQAAAIADgAQAADsAABkBOwEdATsBPQE7AT0BOwE9ATsBHQErAR0BKwEdASsBHQE7AR0BOwEdATsBHQErAT0BKwE9ASsBHQErAYCAQEBAQEBAgIBAQEBAQEBAQEBAQEDAwEBAQECAgAJAAcDAwEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQICAAAEAgACAA4AEAAALAAATETsBGQEpAR0BKQGAgIABAAEA/oD+gAJAAcD+gP6AQEAAAAEAAACAA4AEAAAnAAAZATsBHQE7AR0BOwE9ATsBPQE7ARkBKwE9ASsBHQErAT0BKwEdASsBgIBAQEBAQECAgICAQEBAQEBAgIACQAHAQEBAQEBAQED+QP5AwMBAQEBAwMAAAAABAAAAgAOABAAAJwAAGQE7AR0BOwEdATsBHQE7AT0BOwEZASsBPQErAT0BKwE9ASsBHQErAYCAQEBAQEBAgICAgEBAQEBAQICAAkABwEBAQEBAQMDA/kD+QEBAQEBAQMDAAAAAAgAAAIADgAQAABcAHwAAEzUpAR0BOwEZASsBHQEpAT0BKwEZATsBARErARkBOwGAAUABQEBAQED+wP7AQEBAQAIAwMDAwAPAQEBA/sD+wEBAQEABQAFA/sABQP7A/sAAAgAAAIADgAQAABMAGwAAGQEpAR0BOwEdASsBHQEpAR0BKwEBNSsBHQE7AQGAAYBAQEBA/wD/AICAAoDAwMDAAkABwEBAwMBAQICAAkDAwMAAAAAAAgAAAIADgAQAACMAMwAAEzUpAR0BOwEZASsBHQE7AR0BKwE9ASsBHQEpAT0BKwEZATsBBTUrARkBOwE9ASsBPQE7AYABQAFAQEBAQEBAQEBAQP8A/wBAQEBAAgDAwICAQECAgAPAQEBA/wD/AEBAQEBAQEBAQEABQAFAwMD+wP7AQEBAQAAAAAIAAACAA4AEAAAjAC8AABkBKQEdATsBHQErAR0BOwEdATsBHQErAT0BKwE9ASsBHQErAQE1KwEdATsBPQE7AQGAAYBAQICAQEBAQMDAQEBAQICAAoDAwICAQEACQAHAQEDAwEBAQEBAQEBAQECAgAKAgMDAQEAAAAEAAACAA4AEAAA3AAATNSkBHQE7AR0BKwE9ASsBHQEpAR0BOwEdASsBHQEpAT0BKwE9ATsBHQE7AT0BKQE9ASsBPQE7AYABAAEAQECAgICAAQABAEBAQED+wP7AQECAgMDA/wD/AEBAQEADwEBAQEBAQECAgEBAgIBAQEBAQEBAQICAQECAgAABAIAAgAOABAAADwAAEzUpAR0BKwEZASsBGQErAYABgAGAgICAgICAA8BAQED+gP6AAYABgAAAAAEAAACAA4AEAAAXAAAZATsBGQE7ARkBOwEZASsBHQEpAT0BKwGAgMDAgIBAQP7A/sBAQAKAAYD+gP6AAYABgP6A/oBAQEBAAAEAAACAA4AEAAAvAAAZATsBHQE7AR0BOwE9ATsBPQE7ARkBKwEdASsBHQErAR0BKwE9ASsBPQErAT0BKwGAgEBAQEBAQICAQEBAQEBAQEBAQEBAQEADAAEAwMBAQEBAwMD/AP8AQEBAQEBAQEBAQEBAAAAAAQAAAIADgAQAACcAABkBOwEdATsBPQE7AR0BOwE9ATsBGQErAT0BKwE9ASsBHQErAR0BKwGAgEBAQEBAQICAgIBAQEBAQECAgAJAAcDAwEBAQEDAwP5A/kBAQEBAQEBAQAAAAAEAAACAA4AEAABHAAARNTsBHQE7AR0BOwE9ATsBPQE7AR0BKwEdASsBHQE7AR0BOwEdASsBPQErAT0BKwEdASsBHQErAT0BOwE9ATsBPQErAT0BKwGAgEBAQEBAQICAQEBAQEBAQECAgEBAQEBAQICAQEBAQEBAQEADgIBAQEBAQEBAQICAQEBAQEBAgIBAQEBAQEBAQICAQEBAQEBAAAABAIAAgAOABAAAHwAAEzU7AR0BOwE9ATsBHQErAR0BKwEdASsBPQErAT0BKwGAgICAgICAQEBAQICAQEBAQANAwMDAwMDAwEBAwMDAwEBAAAEAAACAA4AEAAAvAAARNSkBHQErAR0BKwEdASsBHQErAR0BKQEdASkBPQE7AT0BOwE9ATsBPQE7AT0BKQEBwAHAQEBAQEBAQEABAAEA/kD+QEBAQEBAQEBA/wD/AAPAQICAQEBAQEBAQEBAQICAQEBAQEBAQEAAAAEBAACAAwAEAAAPAAABESkBHQErARkBOwEdASkBAQABAAEAgICAgP8A/wACQAHAQED+wP7AQEAAAQAAAIADgAQAADcAABE1OwEdATsBHQE7AR0BOwEdATsBHQE7AR0BOwEdASsBPQErAT0BKwE9ASsBPQErAT0BKwE9ASsBQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAPAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAABAIAAgAKABAAADwAAEzUpARkBKQE9ATsBGQErAYABAAEA/wD/AICAgIADwED+QP5AQEABQAFAAAEAgAMAAwAEAAAXAAABNTsBHQE7AR0BKwE9ASsBHQErAT0BOwEBAMDAQECAgEBAgIBAQAPAQEBAQEBAQEBAQEAAAAAAAQAAAAADgACAAAcAAD0BKQEdASkBAcABwP5A/kBAQEBAAAAAAQGAAwACgAQAAA8AAAE1OwEdATsBHQErAT0BKwEBgEBAQEBAQEBAA8BAQEBAQEBAAAAAAAIAAACAA4ADAAAbACMAABM1KQEdATsBGQEpAT0BKwE9ATsBPQEpAT0BKQEBNSsBHQE7AYABQAFAQED+gP6AQEBAQAEAAQD/AP8AAgDAwMDAAsBAQED/AP8AQEBAQEBAQED+wEBAQAAAAgAAAIADgAQAABcAHwAAGQE7AR0BKQEdATsBHQErAR0BKQE9ASsBJTUrAR0BOwGAgAEAAQBAQEBA/sD+wEBAAoDAwMDAAoABgICAQEDAwEBAQEDAwMDAAAEAAACAA4ADAAAXAAATNSkBHQEpAR0BKQEdASkBPQErAT0BOwGAAYABgP7A/sABQAFA/oD+gEBAQEACwEBAQMDAQEBAQMDAAAIAAACAA4AEAAATABsAAAE1OwEZASkBPQErAT0BOwE9ASkBETUrAR0BOwECgICA/oD+gEBAQEABAAEAwMDAwAOAgP5A/kBAQMDAQED+wMDAwAAAAAIAAACAA4ADAAAbACMAABM1KQEdATsBHQEpAR0BKQEdASkBPQErAT0BOwEFNSsBHQE7AYABQAFAQED+wP7AAQABAP7A/sBAQEBAAgDAwMDAAsBAQECAgEBAQEBAQMDAQEBAQAABAIAAgAOABAAAHwAAATU7AR0BKwEdATsBHQErARkBKwEZASsBPQE7AT0BOwECAMDAgICAgICAgICAgICAQEADwEBAQEBAQED/AP8AAQABAEBAQEAAAAAAAgAAAAADgAMAABsAIwAAEzUpARkBKwEdASkBPQEpAT0BKQE9ASsBPQE7AQU1KwEdATsBgAGAAYBAQP7A/sABAAEA/wD/AEBAQEACAMDAwMACwED+wP7AQEBAQEBAQECAgICAgIAAAAABAAAAgAOABAAAFwAAGQE7AR0BKQEdATsBGQErARkBKwEZASsBgIABAAEAQECAgMDAgIACQAHAgIBAQP8A/wABAAEA/wD/AAACAIAAgAOABAAAEwAbAAABNTsBGQE7AR0BKQE9ATsBPQErARM1OwEdASsBAQDAwICA/oD+gICAQECAgICAgALAQP8A/wBAQEBAwMABQEBAQAAAAAACAIAAAAMABAAAEwAbAAABNTsBGQErAR0BKQE9ATsBGQErARM1OwEdASsBAYDAwEBA/wD/AMDAQECAgICAgALAQP7A/sBAQEBAAQABAAFAQEBAAAABAAAAgAOABAAAKwAAGQE7AR0BOwE9ATsBHQErAR0BKwEdATsBHQE7AR0BKwE9ASsBPQErAR0BKwGAgICAwMBAQEBAQEBAQMDAQEBAQICAAkABwMDAQEBAQEBAQEBAQEBAQEBAQICAAAEAgACAA4AEAAATAAABNTsBGQE7AR0BKQE9ATsBGQErAQEAwMCAgP6A/oCAgEBAA8BA/oD+gEBAQEABQAFAAAABAAAAgAOAAwAAGwAAGQEpAR0BOwEZASsBGQErARkBKwEZASsBGQErAQGAAYBAQICAQECAgEBAQEABwAFAQED/AP8AAQABAP8A/wABAAEA/wD/AAABAAAAgAOAAwAAEwAAGQEpAR0BOwEZASsBGQErARkBKwEBgAGAQECAgMDAgIABwAFAQED/AP8AAQABAP8A/wAAAgAAAIADgAMAABcAHwAAEzUpAR0BOwEdASsBHQEpAT0BKwE9ATsBBTUrAR0BOwGAAUABQEBAQED+wP7AQEBAQAIAwMDAwALAQEBAwMBAQEBAwMDAwMDAAAIAAAAAA4ADAAATABsAABkBKQEdATsBHQErAR0BKQEdASsBATUrAR0BOwEBgAGAQEBAQP8A/wCAgAKAwMDAwAGAAYBAQICAQECAgAIAgICAAAAAAAIAAAAAA4ADAAATABsAABM1KQEZASsBPQEpAT0BKwE9ATsBBTUrAR0BOwGAAYABgICA/wD/AEBAQEACAMDAwMACwED+gP6AgIBAQICAgICAgAAAAAEAgACAA4ADAAAXAAATETsBHQE7AT0BOwEdASsBHQErAR0BKwGAgIBAQMDAwMBAQICAAcABQEBAQEBAQEBAwMAAAAAAAQAAAIADgAMAACcAABM1KQEdASkBHQEpAR0BOwEdASsBHQEpAT0BKQE9ASkBPQErAT0BOwGAAUABQP8A/wABAAEAQEBAQP6A/oABQAFA/wD/AEBAQEACwEBAQEBAQEBAQEBAQEBAQEBAQEAAAQCAAIADgAQAABcAAAE1OwEdATsBHQErARkBKwEZASsBPQE7AQGAgICAgICAgICAgICAA4CAgIBAQP8A/wABAAEAQEAAAAAAAQAAAIADgAMAABMAABkBOwEZATsBGQE7ARkBKQE9ASsBgIDAwICA/oD+gEBAAgABAP8A/wABAAEA/sD+wEBAAAEAgACAA4ADAAAfAAATNTsBHQE7AT0BOwEdASsBHQErAR0BKwE9ASsBPQErAYCAgICAgIBAQEBAgIBAQEBAAkDAwMDAwMDAQEBAQEBAQEAAAQAAAIADgAMAABsAABkBOwEZATsBGQE7ARkBOwEZATsBGQEpAT0BKwFAQEBAgIBAQICA/oD+gEBAAgABAP8A/wABAAEA/wD/AAEAAQD+wP7AQEAAAQAAAIADgAMAACcAABE1OwEdATsBPQE7AR0BKwEdATsBHQErAT0BKwEdASsBPQE7AT0BKwGAgMDAgICAgICAgIDAwICAgICAgAKAgEBAQECAgEBAgIBAQEBAgIBAQAAAAQAAAAADgAMAAB8AABE1OwEdATsBPQE7ARkBKwEdASkBPQEpAT0BKQE9ASsBgIDAwICAQED+wP7AAQABAP8A/wBAQAJAwMDAwMD+wP7AQEBAQEBAQEAAAAEAAACAA4ADAAAnAAARNSkBHQErAR0BKwEdASsBHQE7AR0BKQE9ATsBPQE7AT0BOwE9ASsBAcABwEBAQEBAQMDA/kD+QEBAQEBAQMDAAsBAQEBAQEBAQEBAQEBAQEBAQEBAAAABAQAAgAMABAAAJwAAATU7AR0BKwEdASsBHQE7AR0BOwEdASsBPQErAT0BKwE9ATsBPQE7AQIAgIBAQEBAQEBAQICAQEBAQEBAQEADwEBAQICAQECAgEBAQECAgEBAgIAAAAAAAQGAAIACgAQAAAcAAAEROwEZASsBAYCAgICAAkABwP5A/kAAAQCAAIACgAQAACcAABM1OwEdATsBHQE7AR0BKwEdASsBHQErAT0BOwE9ATsBPQErAT0BKwGAgIBAQEBAQEBAQICAQEBAQEBAQEADwEBAQICAQECAgEBAQECAgEBAgIAAAQAAAYADgAMAACcAABM1OwEdATsBHQE7AT0BOwEdASsBHQErAT0BKwE9ASsBHQErAT0BOwGAwMBAQEBAQEBAQMDAQEBAQEBAQEACwEBAQEBAQEBAQEBAQEBAQEBAQEAAAgCAAIADAAGAAAcADwAAATU7AR0BKwElNTsBHQErAQIAgICAgP6AgICAgAEAgICAgICAgAAAAAACAQAAgAKABAAACwATAAABNTsBGQErAT0BOwERNTsBHQErAQGAgIDAwEBAgICAgAKAgP7A/sDAwAHAQEBAAAAAAgAAAIADgAQAADcAPwAAATU7AR0BOwEdATsBHQErAT0BKwEdATsBPQE7AR0BKwEdASsBHQErAT0BKwE9ASsBPQE7AT0BOwERNSsBHQE7AQGAQECAgEBAgIBAQEBAgIBAQICAQECAgEBAQECAgEBAQEADwEBAQEBAQEBAQMDAQEBAQEBAQEBAQEBAwMBAQP7AwMDAAAEAAACAA4AEAAAvAAABNSkBHQE7AR0BKwE9ASsBHQE7AR0BKwEdASkBHQEpAT0BOwE9ASsBPQE7AT0BOwEBAAEAAQBAQICAgIDAwMDAAQABAP5A/kBAQEBAQEBAQAPAQEBAQEBAQICAQECAgEBAQECAgEBAgIAAAAIAgAEAA4ADgAA3AD8AABM1OwEdATsBPQE7AR0BOwE9ATsBHQErAR0BOwEdASsBPQErAR0BKwE9ASsBHQErAT0BOwE9ASsBBTUrAR0BOwGAQEBAQICAQEBAQEBAQEBAQEBAgIBAQEBAQEBAQAIAgICAgANAQEBAQEBAQEBAQEDAwEBAQEBAQEBAQEBAQMDAwMDAwAABAIAAgAOABAAANwAAEzU7AR0BOwE9ATsBHQErAR0BOwEdASsBHQE7AR0BKwEdASsBPQErAT0BOwE9ASsBPQE7AT0BKwGAgICAgICAQEBAQICAgICAgICAgICAgICAQEBAQAOAgICAgICAgEBAQEBAQEBAQEBAQEBAQEBAQEBAAAIBgACAAoAEAAAHAA8AAAE1OwEdASsBETU7AR0BKwEBgICAgICAgICAAUDAwMACwMDAwAAEAIAAgAOABAAALwA3AD8ARwAAATUpAR0BOwEdASsBHQE7AR0BOwEdASsBHQEpAT0BKwE9ATsBPQErAT0BKwE9ATsBBTUrAR0BOwEVNSsBHQE7ARU1KwEdATsBAQABAAEAQECAgEBAQEBAQP8A/wBAQICAQEBAQEBAAYCAgICAgICAgICAgIADwEBAQEBAQEBAQICAQEBAQEBAQEBAQICAQEBAQMBAQEDAQEBAAAAAAAIAgAOAAwAEAAAHAA8AAAE1OwEdASsBJTU7AR0BKwECAICAgID+gICAgIADwEBAQEBAQEAAAAAAAwAAAAAEAAQAABcAPwBXAAABNTsBHQErAR0BOwEdASsBPQErAT0BOwEDNSkBHQE7AR0BOwEZASsBHQErAR0BKQE9ASsBPQErARkBOwE9ATsBBTUpAR0BKwEZATsBHQEpAT0BOwEZASsBAYCAgICAgICAgEBAQECAAQABAEBAQEBAQEBA/wD/AEBAQEBAQEBAAgD/AP8AQEBAQAEAAQBAQEBAAsBAQECAgEBAQECAgAFAQEBAQED/AP8AQEBAQEBAQEABAAEAQEBAQEBA/wD/AEBAQEABAAEAAAAAAAIAgAIAAwAEAAAXAB8AABM1KQEdATsBHQEpAT0BKwE9ATsBPQErAQU1KwEdATsBgAEAAQBAQP8A/wBAQEBAQEABgEBAQEADwEBAQMDAQEBAQEBAwEBAQAACAAABAAOAA4AANwBPAAABNTsBHQE7AT0BOwEdASsBHQErAR0BOwEdATsBHQErAT0BKwEdASsBPQErAT0BKwE9ATsBPQE7AQU1KwEdASsBHQE7AR0BOwE9ASsBPQE7AQEAgIBAQICAQEBAQEBAQECAgEBAgIBAQEBAQEBAQAEAQEBAQEBAQEBAQEBAA0BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAQCAAYADgAMAAAsAABM1KQEdASsBPQEpAYABgAGAgID/AP8AAsBAwMCAgAABAIACAAOAAoAABwAAEzUpAR0BKQGAAYABgP6A/oACQEBAQAAEAAAAAAQABAAAGwAjAEsAYwAAARE7AR0BOwEdASsBHQE7AR0BKwE9ASsBHQErAQE1KwEdATsBATUpAR0BOwEdATsBGQErAR0BKwEdASkBPQErAT0BKwEZATsBPQE7AQU1KQEdASsBGQE7AR0BKQE9ATsBGQErAQEAwMBAQEBAQEBAQICAQEABgICAgID+gAEAAQBAQEBAQEBAQP8A/wBAQEBAQEBAQAIA/wD/AEBAQEABAAEAQEBAQAIAAQBAQEBAQEBAQEBAQEABQEBAQAHAQEBAQED/AP8AQEBAQEBAQEABAAEAQEBAQEBA/wD/AEBAQEABAAEAAAEAgAOAAwAEAAAHAAATNSkBHQEpAYABQAFA/sD+wAPAQEBAAAIBAAKAAoAEAAAXAB8AAAE1OwEdATsBHQErAR0BKwE9ASsBPQE7ARc1KwEdATsBAYBAQEBAQEBAQEBAQECAQEBAQAPAQEBAQEBAQEBAQEBAQEBAAAIAgACAA4AEAAAHAB8AADc1KQEdASkBATU7AR0BOwEdASsBHQErAT0BKwE9ATsBgAGAAYD+gP6AAQCAgICAgICAgICAgIDAQEBAAwCAgIBAQICAgIBAQAABAQACAAMABAAAHwAAATU7AR0BOwEdASsBHQE7AR0BKQE9ATsBPQE7AT0BKwEBAMDAQEBAQEBA/wD/AEBAQECAgAPAQEBAQEBAQEBAQEBAQEBAAAABAQACAAMABAAAHwAAATUpAR0BKwEdATsBHQErAR0BKwE9ATsBPQErAT0BKwEBAAEAAQBAQEBAQEDAwICAQEBAQAPAQEBAQEBAQEBAQEBAQEBAAAABAYADAAKABAAADwAAATU7AR0BKwEdASsBPQE7AQIAQEBAQEBAQEADwEBAQEBAQEAAAAAAAQAAAAADgAMAAB8AABkBOwEZATsBGQE7ARkBOwEdASsBPQErAR0BKwEdASsBgICAgICAQECAgEBAgICAgAGAAYD/AP8AAQABAP8A/wBAQEBAQEBAQAAAAAMAgACAA4AEAAAbACsAMwAAATUpARkBKwE9ASsBHQErAT0BKwE9ASsBPQE7AQU1KwEdASsBHQE7AR0BOwElNSsBHQE7AQEAAUABQEBAQEBAQICAQEBAQAEAQEBAQEBAQEABAEBAQEADwED+QP5AgICAgICAQEDAwMDAQEBAQEBAwMDAwAAAAAABAQABgAIAAoAABwAAATU7AR0BKwEBAICAgIACAICAgAAAAAABAQAAAAKAAQAADwAAJTU7AR0BKwEdASsBPQE7AQIAQEBAQICAgIDAQEBAQEBAQAABAQACAAMABAAAFwAAATU7AR0BOwEdASkBPQE7AT0BKwE9ATsBAYCAgEBA/wD/AEBAQEBAQAPAQMDAQEBAQEBAQEAAAAIAgAIAAwAEAAAXAB8AAAE1OwEdATsBHQErAR0BKwE9ASsBPQE7AQU1KwEdATsBAQDAwEBAQEDAwEBAQEABAEBAQEADwEBAQICAQEBAQICAgICAgAAAAAACAAABAAOAA4AANwBPAAARNTsBHQE7AT0BOwEdATsBHQE7AR0BKwEdASsBHQErAT0BKwEdASsBPQE7AT0BOwE9ASsBPQErAQU1KwEdATsBHQErAR0BOwE9ATsBPQErAYCAQECAgEBAQEBAQEBAgIBAQICAQEBAQEBAQEACAEBAQEBAQEBAQEBAQANAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAACAAAAgAOABAAAEwBbAAABNTsBGQErAT0BKwE9ATsBPQE7AQE1OwEZATsBPQE7AT0BOwE9ATsBPQE7AR0BKwEdASsBHQErAR0BKwEdASsBHQErAR0BKwE9ATsBPQE7AT0BKwE9ASsBPQE7AQMAQEBAQICAQEBAQP2AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAkBA/wD/AEBAQEBAQAHAQP8A/wBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAgIBAQAAAAAEAAACAA4AEAABjAAATNTsBGQE7AT0BOwE9ATsBPQE7AT0BOwEdASsBHQErAR0BOwEdASsBHQE7AR0BKwE9ATsBPQE7AT0BKwE9ASsBHQErAR0BKwEdASsBHQErAT0BOwE9ATsBPQErAT0BKwE9ATsBgEBAQEBAQEBAQEBAQEBAQECAgEBAQEDAwEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAA8BA/wD/AEBAQEBAQEBAQEBAQEBAgIBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQICAQEAAAAADAAAAgAOABAAAEwBjAGsAAAE1OwEZASsBPQErAT0BOwE9ATsBATU7AR0BKwEdATsBHQE7AT0BOwE9ATsBPQE7AR0BKwEdASsBHQErAR0BKwEdASsBHQErAR0BKwE9ATsBPQE7AT0BKwE9ATsBPQErAT0BKwEBNSsBHQE7AQMAQEBAQICAQEBAQP0AwMBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA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AQDAwEBAQEBAQEBAgICAgEBAQEDAwICAQEBAQAEAQEBAQMDAQEADwEBAQEBAwMBAQEBAQEBAQEBAgIBAQMDAQEBAQEBAQEBAQAAAAAACAAAAAAOABAAAMwA7AAATNSkBHQE7ARkBKwEdASsBHQE7AR0BKwE9ASsBPQE7AT0BKwE9ASsBPQE7AT0BKQE9ASkBATUrAR0BOwGAAUABQEBAwMBAQICAgIBAQEBAgIBAQEBAAQABAP8A/wACAMDAwMADwEBAQP8A/wBAQEBAQEBAQEBAQEBAQEBAQEBAQP7AQEBAAAAAAAEAAACAA4AEAABHAAABNTsBHQErAR0BOwEdATsBHQErAT0BKwEdASsBHQE7AR0BOwE9ATsBHQErAR0BKQE9ASsBPQErAT0BOwE9ATsBPQE7AT0BOwECAEBAQECAgEBAgICAgEBAQECAgICAQED/AP8AQEBAQEBAQEBAQEBAA8BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAQAAAIADgAQAACcAAAE1OwEdASsBHQE7AR0BKQEdASkBHQEpAT0BKwE9ATsBPQE7AT0BOwECAEBAQEDAwP7A/sABQAFA/oD+gEBAQECAgEBAA8BAQEBAQEBAwMBAQEBAwMBAQEBAAAACAAAAgAOABAAAQwBLAAABNTsBHQE7AR0BOwEdASsBPQErAR0BKwEdATsBHQE7AT0BOwEdASsBHQEpAT0BKwE9ASsBPQE7AT0BOwE9ASsBPQE7AQU1KwEdATsBAQDAwEBAQECAgICAQEBAQICAgIBAQP8A/wBAQEBAQEBAQEBAQEABAEBAQEADwEBAQICAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAIAAACAA4AEAAAjACsAAAE1OwEdATsBHQE7AR0BKQEdASkBHQEpAT0BKwE9ATsBPQE7AQU1KwEdATsBAQDAwEBAQED+wP7AAUABQP6A/oBAQEBAQEABAEBAQEADwEBAQEBAQEDAwEBAQEDAwICAQEBAQAAAAgAAAIADgAQAADcAPwAAATUpAR0BOwEdASsBPQErAR0BKwEdATsBHQE7AT0BOwEdASsBHQEpAT0BKwE9ASsBPQE7AT0BOwETNTsBHQErAQEAAQABAEBAgICAgEBAQECAgICAQED/AP8AQEBAQEBAQECAgICAgALAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAUBAQEAAAAAAAgAAAIADgAQAABcAHwAAEzUpAR0BKQEdASkBHQEpAT0BKwE9ATsBATU7AR0BKwGAAYABgP7A/sABQAFA/oD+gEBAQEABAICAgIACwEBAQMDAQEBAQMDAAUBAQEAAAAAAAQAAAIADgAQAAEcAABM1OwEdATsBPQE7AR0BKwEdATsBHQErAT0BKwEdASsBHQE7AR0BOwE9ATsBHQErAR0BKQE9ASsBPQErAT0BOwE9ATsBPQErAYCAgICAgIBAQEBAgICAgEBAQECAgICAQED/AP8AQEBAQEBAQEBAQAPAQEBAQEBAQICAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAgIAAAAABAAAAgAOABAAALwAAEzU7AR0BOwE9ATsBHQErAR0BOwEdASkBHQEpAR0BKQE9ASsBPQE7AT0BOwE9ASsBgICAQECAgEBAgID+wP7AAUABQP6A/oBAQEBAQEBAQAPAQEBAQEBAQEBAQEDAwEBAQEDAwEBAQEAAAAACAAAAgAOABAAAKwA7AAATNTsBHQE7AT0BOwEdASsBHQE7AR0BOwEdASsBHQErAR0BKQEZATsBPQErAQE1KwEdATsBPQE7AT0BKwGAgIBAQICAQEBAQEBAQEBAQP7A/sCAgEBAAYCAgICAQEBAQAPAQEBAQEBAQICAQEBAQEBAQEABQAFAQED+wEDAwEBAQEAAAAAAAwAAAIAEAAQAAAsAHwAnAAABNTsBHQErAR0BKwEBNTsBGQEpAT0BKwE9ATsBPQE7ARE1KwEdATsBAwCAgEBAQED+gICA/wD/AEBAQECAgEBAQEADQMCAgEBAAQCA/kD+QEBAwMBAQP7AwMDAAAAAAAIAAACAA4AEAAAfADcAABM1KQEdATsBHQE7AR0BKwEdASsBHQEpAT0BKwE9ATsBJTUrAR0BOwEdASsBHQE7AT0BOwE9ASsBgAEAAQBAQEBAQEBAQP8A/wBAQEBAAYBAQEBAQEBAQEBAQEADQMBAQEBAwMBAQEBAwMBAQMBAgIBAQICAQEDAwAACAAAAgAOABAAAHwAnAAABNTsBHQE7AR0BKwEZASkBPQErAT0BOwE9ATsBPQE7ARE1KwEdATsBAgCAgEBAQED+wP7AQEBAQICAQECAgICAA8BAQEBAQP7A/sBAQMDAQEBAQP5AwMDAAAIAAACAA4AEAAAXAB8AABkBKQEdASkBHQEpAR0BKQEdASkBHQEpARM1KQEdASkBAcABwP7A/sABAAEA/wD/AAFAAUD+QP5AgAFAAUD+wP7AAcABQEBAQEBAQEBAQEADQEBAQAADAAAAgAOABAAAGwAjACsAABM1KQEdATsBHQEpAR0BKQEdASkBPQErAT0BOwEFNSsBHQE7AQE1KQEdASkBgAFAAUBAQP7A/sABAAEA/sD+wEBAQEACAMDAwMD+AAFAAUD+wP7AAsBAQECAgEBAQEBAQMDAQEBAQAHAQEBAAAAAAAEAAACAA4AEAAAvAAATNTsBHQE7AT0BOwEdASsBHQE7AR0BKQEdASkBHQEpAR0BKQEdASkBGQE7AT0BKwGAQEDAwEBAQECAgP7A/sABAAEA/wD/AAFAAUD+QP5AgIBAQAPAQEBAQEBAQEBAQEBAQEBAQEBAQAFAAUBAQAACAAAAgAOABAAAMwA7AAATNTsBHQE7AT0BOwEdASsBHQE7AR0BOwEdASkBHQEpAR0BKQE9ASsBPQE7AT0BOwE9ASsBATUrAR0BOwGAQEDAwEBAQEBAQEBA/sD+wAEAAQD+wP7AQEBAQEBAQEACAMDAwMADwEBAQEBAQEBAQEBAgIBAQEBAQEDAwEBAQED+wEBAQAAAAgAAAIADgAQAABcAHwAAGQEpAR0BKQEdASkBHQEpAR0BKQEdASkBATU7AR0BKwEBwAHA/sD+wAEAAQD/AP8AAUABQP5A/kABgICAgIABwAFAQEBAQEBAQEBAQANAQEBAAAAAAAMAAACAA4AEAAAbACMAKwAAEzUpAR0BOwEdASkBHQEpAR0BKQE9ASsBPQE7AQU1KwEdATsBATU7AR0BKwGAAUABQEBA/sD+wAEAAQD+wP7AQEBAQAIAwMDAwP8AgICAgALAQEBAgIBAQEBAQEDAwEBAQEABwEBAQAAAAAABAAAAAAOABAAALwAAGQEpAR0BKQEdASkBHQEpAR0BKQEdASsBHQErAR0BOwEdASsBPQErAT0BOwE9ASsBAcABwP7A/sABAAEA/wD/AAFAAUDAwEBAgICAgEBAQEDAwALAAUBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAgAAAAADgAQAADMAOwAAEzUpAR0BOwEdASkBHQEpAR0BKwEdASsBHQE7AR0BKwE9ASsBPQE7AT0BKwE9ASsBPQE7AQU1KwEdATsBgAFAAUBAQP7A/sABAAEAgIBAQICAgIBAQEBAgIBAQEBAAgDAwMDAA8BAQECAgEBAQEBAQEBAQEBAQEBAQEBAQMDAQEBAQAAAAAEAAACAA4AEAAAvAAATNTsBHQE7AT0BOwEdASsBHQE7AR0BKQEdASkBHQEpAR0BKQEdASkBGQE7AT0BKwGAgIBAQICAQECAgP7A/sABAAEA/wD/AAFAAUD+QP5AgIBAQAPAQEBAQEBAQEBAQEBAQEBAQEBAQAFAAUBAQAACAAAAgAOABAAAMwA7AAATNTsBHQE7AT0BOwEdASsBHQE7AR0BOwEdASkBHQEpAR0BKQE9ASsBPQE7AT0BOwE9ASsBATUrAR0BOwGAgIBAQICAQEBAQEBA/sD+wAEAAQD+wP7AQEBAQEBAQEACAMDAwMADwEBAQEBAQEBAQEBAgIBAQEBAQEDAwEBAQED+wEBAQAAAAgAAAIADgAQAACsAMwAAATU7AR0BOwEdATsBHQEpAR0BOwE9ASsBPQE7AR0BKQE9ASsBPQE7AT0BOwEFNSsBHQE7AQEAwMBAQEBA/sD+wMDAQEDAwP6A/oBAQEBAQEABAEBAQEADwEBAQEBAQEDAwEBAQEDAwEBAwMCAgEBAQEAAAAAAAwAAAAADgAQAACcALwA3AAABNTsBHQE7AR0BOwEZASsBHQEpAT0BKQE9ASkBPQErAT0BOwE9ATsBBTUrAR0BOwETNSsBHQE7AQEAwMBAQEBAQED+wP7AAQABAP8A/wBAQEBAQEABAEBAQECAwMDAwAPAQEBAQED+wP7AQEBAQEBAQECAgICAQEBAQP8AgICAAAAAAAEAAACAA4AEAAA3AAATNTsBHQE7AT0BOwEdASsBHQE7AR0BKQEdATsBPQErAT0BOwEdASkBPQErAT0BOwE9ATsBPQErAYBAQMDAQEBAQICA/sD+wMDAQEDAwP6A/oBAQEBAQEBAQAPAQEBAQEBAQEBAQEDAwEBAQEDAwEBAwMBAQEBAAAIAAAAAA4AEAAAzADsAABM1OwEdATsBPQE7AR0BKwEdATsBGQErAR0BKQE9ASkBPQEpAT0BKwE9ATsBPQE7AT0BKwEBNSsBHQE7AYBAQMDAQEBAQICAQED+wP7AAQABAP8A/wBAQEBAQEBAQAIAwMDAwAPAQEBAQEBAQEBA/sD+wEBAQEBAQEBAgIBAQEBA/oCAgIAAAAAAAgAAAIADgAQAAB8AJwAAEzUpAR0BKQEdATsBPQErAT0BOwEdASkBPQErAT0BOwEBNTsBHQErAYABgAGA/sD+wMDAQEDAwP6A/oBAQEBAAQCAgICAAsBAQEDAwEBAQEDAwEBAwMABQEBAQAAAAwAAAAADgAQAABsAIwArAAATNSkBGQErAR0BKQE9ASkBPQEpAT0BKwE9ATsBBTUrAR0BOwEBNTsBHQErAYABgAGAQED+wP7AAQABAP8A/wBAQEBAAgDAwMDA/wCAgICAAsBA/sD+wEBAQEBAQEBAgICAgICAAkBAQEAAAAEAAAAAA4AEAAA/AAABNSkBHQEpAR0BKwEdATsBHQE7AT0BKwE9ATsBHQErAR0BKwEdASsBPQE7AT0BKwE9ASsBPQErAT0BOwE9ATsBAQABQAFA/wD/AEBAQECAgEBAwMCAgEBAgIBAQEBAQEBAQEBAQEADwEBAQEBAgIBAQEBAQEDAwEBAQEBAQEBAQEBAQICAQEAAAAAAAgAAAAADgAQAACsAMwAAATU7AR0BKwEdATsBGQErAR0BKQE9ASkBPQEpAT0BKwE9ATsBPQE7AT0BOwEBNSsBHQE7AQGAgIBAQMDAQED+wP7AAQABAP8A/wBAQEBAQEBAQAEAwMDAwAPAQEBAQED+wP7AQEBAQEBAQECAgEBAQED+gICAgAAAAAIAAACAA4AEAAAXACcAAAE1OwEdATsBGQErAT0BKwEdASsBGQE7AQU1KwEdASsBHQE7AT0BKwEBAMDAgICAgMDAgICAgAEAQEBAQMDAQEADwEBAQP6A/oCAgICAAYABgEBAQECAgICAAAAAAAEAAACAA4AEAAArAAABNTsBHQE7AR0BKwE9ASsBHQErAR0BKQEdATsBHQErAT0BKwEdASsBGQE7AQEAwMBAQICAQEBAQAEAAQBAQICAwMCAgICAA8BAQEBAQEBAQEBAQEBAwMDAwMDAAYABgAAAAAACAAAAgAOABAAAFwAfAAAZATsBHQE7AT0BOwEZASsBPQErAR0BKwEBNSsBHQE7AYCAwMCAgICAwMCAgAKAwMDAwAJAAcBAQEBA/kD+QMDAwMACQEBAQAAAAQAAAIADgAQAACMAABM1OwEdATsBHQE7AR0BOwEZASsBGQErARkBKwEZASsBPQE7AYCAgEBAgIBAQICAgICAgEBAQEADwEBAQEBAQED/AP8AAQABAP8A/wABQAFAQEAAAgCAAIADgAQAACsAMwAAATU7AR0BOwE9ATsBHQErAR0BOwEdASsBHQE7AR0BKQE9ATsBPQErAT0BOwEFNSsBHQE7AQEAgICAgEBAQEBAQICAgID+gP6AgICAgEBAAQCAgICAA8BAQEBAQEBAQEBAQMDAQEBAQMDAgIBAQEBAAAACAIAAgAOABAAAEwAzAAABNTsBHQE7AR0BKQE9ATsBPQErARE1OwEdATsBPQE7AR0BKwEdASsBPQErAR0BKwE9ATsBAQDAwICA/oD+gICAQECAgICAQEBAQICAgIBAQEBAAkBAwMBAQEBAgIABwEBAQEBAQEBAQEBAQEBAQAAAAAIAgACAA4AEAAAXAB8AABM1KQEdASsBHQE7AR0BKQE9ATsBPQErARE1KQEdASkBgAGAAYCAgICA/oD+gICAgIABgAGA/oD+gALAQEBAwMBAQEBAwMABQEBAQAAAAgCAAIADgAQAABMAGwAAATU7ARkBOwEdASkBPQE7AT0BKwEDNSkBHQEpAQEAwMCAgP6A/oCAgEBAgAFAAUD+wP7AAsBA/wD/AEBAQEDAwAFAQEBAAAAAAAEAgACAA4AEAAAvAAABNTsBHQE7AT0BOwEdASsBHQE7AR0BKwEdATsBHQEpAT0BOwE9ASsBPQE7AT0BKwEBAEBAgIBAQEBAgICAgICA/oD+gICAgICAgEBAA8BAQEBAQEBAQEBAQMDAQEBAQMDAQEBAQAAAAQCAAIADgAQAACMAABM1OwEdATsBPQE7AR0BKwEZATsBHQEpAT0BOwE9ASsBPQErAYBAQMDAQEBAQICA/oD+gICAQEBAQAPAQEBAQEBAQP7A/sBAQEBAwMCAgAABAIAAAAOABAAALwAAEzUpAR0BKwEdATsBHQErAR0BKwEdATsBHQErAT0BKwE9ATsBPQErAT0BOwE9ASsBgAGAAYCAgICAwMBAQICAgIBAQEBAgICAgICAA8BAQEDAwEBAQEBAQEBAQEBAQEBAQEDAwAAAAAIAgAAAA4AEAAArADMAAAE1OwEdATsBHQErAR0BKwEdATsBHQErAT0BKwE9ATsBPQErAT0BOwE9ASsBEzU7AR0BKwEBAMDAgIDAwEBAgICAgEBAQECAgICAQECAgICAgALAQICAQEBAQEBAQEBAQEBAQEBAQEBAAUBAQEAAAAAAAgCAAIADgAQAABcAHwAAEzUpAR0BKwEdATsBHQEpAT0BOwE9ASsBATU7AR0BKwGAAYABgICAgID+gP6AgICAgAEAgICAgALAQEBAwMBAQEBAwMABQEBAQAAAAAABAIAAgAOAAwAAEwAAATU7ARkBOwEdASkBPQE7AT0BKwEBAMDAgID+gP6AgIBAQALAQP8A/wBAQEBAwMAAAAAAAgAAAIADgAQAAA8AJwAAARE7ARkBKwEdASkBPQEpAQE1KQEdASsBHQE7AR0BKQE9ATsBPQErAQKAgIBAQP7A/sABAAEA/YABAAEAQEBAQP8A/wBAQEBAAoABgP6A/oBAQEBAAsBAQEDAwEBAQEDAwAAAAAAEAAAAAAOABAAAEwAfACcALwAAATU7ARkBKwEdASkBPQE7ARkBKwElNTsBGQErAT0BKwEBNTsBHQErASU1OwEdASsBAgDAwEBA/wD/AMDAQED+AMDAgIBAQAKAgICAgP4AgICAgALAQP7A/sBAQEBAAQABAEBA/wD/AMDAAUBAQEBAQEBAAAAAAQAAAIADgAQAACcAAAE1OwEdATsBGQErAR0BKQE9ASsBPQE7AR0BOwEZASsBHQErAT0BOwEBgMDAQEBAQP7A/sBAQICAwMBAQICAQEADwEBAQP7A/sBAQEBAQEBAQAFAAUBAQEBAAAACAIAAAAOABAAAEwArAAABNTsBGQErAR0BKQE9ATsBPQErARE1OwEdATsBHQErAT0BKwEdASsBPQE7AQGAwMBAQP8A/wDAwEBAwMBAQICAQECAgEBAAkBA/wD/AEBAQEDAwAHAQEBAQEBAQEBAQEAAAgAAAAADgAQAADcAQwAAGQE7AR0BOwE9ATsBPQE7AR0BKwEdASsBHQE7AR0BOwEdATsBHQErAR0BKwEdASsBPQE7AT0BKwElNSsBHQE7AT0BKwGAgEBAQECAgEBAQEBAQEBAQEDAwEBAgIBAQICAAYBAQICAQEACgAGAgIBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEDAQICAQEAAAgAAAAADgAQAAC8AOwAAGQE7AR0BOwE9ATsBHQErAR0BKwEdATsBHQE7AR0BKwEdASsBHQErAT0BOwE9ASsBJTUrAR0BOwE9ASsBgICAgMDAQEBAQEBAQEDAwEBAgIBAQICAAYBAQICAQEACgAGAgIBAQEBAQEBAQEBAQEBAQEBAQEBAQMBAgIBAQAABAAAAgAOAAwAALwAAGQE7AR0BOwE9ATsBPQE7AR0BKwEdASsBHQE7AR0BOwEdASsBPQErAT0BKwEdASsBgICAgEBAgIBAQEBAQEBAQICAQECAgICAAcABQICAQEBAQEBAQEBAQEBAQEBAQEBAgIAAAQCAAIADgAQAABcAAAE1OwEdASsBHQErARkBKQEdASkBGQE7AQIAQEBAQEBAAQABAP6A/oDAwAPAQEBAQED/AP8AQEABgAGAAAAAAAEAAACAAwAEAAAjAAABNTsBHQErAR0BOwEZATsBHQEpAT0BOwE9ASsBPQE7AT0BOwEBgEBAQEBAQICA/oD+gICAQEBAQEBAA8BAQEBAQP8A/wBAQEBAwMBAQEBAAAAAAAEAgAAAA4AEAAAbAAATETsBGQEpAR0BKwEdASsBHQErAT0BOwE9ASsBgICAAQABAICAQECAgEBAgIACgAGA/sD+wEBAQEBAQEBAQEAAAAAAAQCAAAADgAQAACMAAAE1OwEZATsBHQErAR0BKwEdASsBPQE7AT0BKwE9ATsBGQErAQEAwMCAgICAQECAgEBAgICAgEBAA8BA/sD+wEBAQEBAQEBAQEBAQAEAAQAAAAAAAgCAAIADgAQAAAsAFwAAATU7AR0BKwEdASsBBRE7ARkBKQEdASkBAoCAgEBAQED+AICAAQABAP6A/oADQMCAgEBAQAHA/oD+gEBAAAIAAACAA4AEAAALAB8AAAE1OwEdASsBHQErAQE1OwEZATsBHQEpAT0BOwEZASsBAoCAgEBAQED+AMDAgID+gP6AgIBAQANAwICAQEABQED+gP6AQEBAQAFAAUAAAgCAAIADgAQAAAcAEwAAATU7AR0BKwElETsBGQEpAR0BKQECAICAgID+gICAAQABAP6A/oACgICAgEABwP6A/oBAQAACAAAAgAOABAAABwAbAAABNTsBHQErAQE1OwEZATsBHQEpAT0BOwEZASsBAoCAgICA/gDAwICA/oD+gICAQEACgICAgAHAQP6A/oBAQEBAAUABQAABAAAAgAOABAAAGwAAEzU7AR0BOwEdASsBHQEpAR0BKQE9ASsBPQE7AYCAgEBAQEABAAEA/oD+gEBAQEADQMCAgEBAwMBAQMDAQEAAAQCAAIADgAQAACMAAAE1OwEdATsBHQErAR0BOwEdASkBPQE7AT0BKwE9ATsBPQErAQEAwMBAQEBAgID+gP6AgIBAQEBAQEADwECAgEBAwMBAQEBAgIBAQICAAAABAAAAgAOABAAALwAAATU7AR0BKwEdASsBHQE7AR0BOwE9ATsBGQErAT0BKwE9ASsBHQErARkBOwE9ATsBAgBAQEBAQEBAQEBAgIDAwEBAQECAgMDAQEADwEBAQEBAQEBAQICA/sD+wEBAQECAgAFAAUBAQAAAAAABAAAAgAOABAAAIwAAATU7AR0BKwEdATsBHQE7ARkBKwEZASsBGQErARkBOwE9ATsBAgBAQEBAgIBAQICAwMCAgMDAQEADwEBAQEBAQED/AP8AAQABAP8A/wABQAFAQEAAAAAAAQAAAAADgAQAADMAABkBOwEdATsBHQE7AT0BOwEZASsBHQErAR0BKwE9ATsBPQE7AT0BKwE9ASsBPQErAR0BKwHAwEBAQECAgICAQECAgEBAgIBAQEBAQECAgAKAAYBAQEBAgID+gP6AQEBAQEBAQEBAQEBAQEDAwAAAAAEAAAAAA4ADgAAjAAAZASkBHQE7ARkBKwEdASsBHQErAT0BOwE9ATsBGQErARkBKwEBgAGAQECAgEBAgIBAQICAwMCAgAJAAUBAQP8A/wBAQEBAQEBAQAEAAQD/AP8AAAIAAACAA4AEAAArADcAABM1OwEdATsBPQE7AR0BKwEdATsBGQErAT0BKwE9ASsBHQErARkBOwE9ASsBATUrAR0BOwEdATsBgICAQECAgEBAgIDAwEBAQECAgICAQEACAICAQEBAQAPAQEBAQEBAQEBA/sD+wEBAQECAgAFAAUBAQP8AgEBAQEAAAAAAAQAAAIADgAQAACsAABM1OwEdATsBPQE7AR0BKwEdATsBHQE7ARkBKwEZASsBGQErARkBOwE9ASsBgICAQECAgEBAQEBAQICAwMCAgICAQEADwEBAQEBAQEBAQEBA/wD/AAEAAQD/AP8AAUABQEBAAAEAAACAA4AEAAAfAAARNTsBHQEpAR0BOwEZASsBGQErARkBKwEZASsBHQErAYCAAQABAEBAgIBAQICAQEBAQANAwICAQED/AP8AAQABAP8A/wABQAFAQEAAAAAAAQAAAAADgAQAACsAABkBOwEdATsBHQE7AT0BOwEZASsBHQEpAT0BOwE9ASsBPQErAT0BKwEdASsBwMBAQEBAgIBAQP8A/wDAwEBAQEBAQICAAoABgEBAQECAgP5A/kBAQEBAgIBAQEBAwMAAAQAAAAADgAOAABsAABkBKQEdATsBGQErAR0BKQE9ATsBGQErARkBKwEBgAGAQEBAQP8A/wDAwMDAgIACQAFAQED+wP7AQEBAQAFAAUD/AP8AAAAAAwAAAIADgAQAABcAHwAnAAATNSkBHQE7AR0BKwEdASkBPQErAT0BOwEFNSsBHQE7AQE1KQEdASkBgAFAAUBAQEBA/sD+wEBAQEACAMDAwMD+AAFAAUD+wP7AAsBAQEDAwEBAQEDAwMDAwMACwEBAQAAAAAADAAAAgAOABAAAFwAfACcAABM1KQEdATsBHQErAR0BKQE9ASsBPQE7AQU1KwEdATsBATUpAR0BKQGAAUABQEBAQED+wP7AQEBAQAIAwMDAwP4AAUABQP7A/sACwEBAQMDAQEBAQMDAwMDAwALAQEBAAAAAAAIAAACAA4AEAAAvADcAABM1OwEdATsBPQE7AR0BKwEdATsBHQE7AR0BKwEdASkBPQErAT0BOwE9ATsBPQErAQE1KwEdATsBgEBAwMBAQEBAQEBAQEBA/sD+wEBAQEBAQEBAAgDAwMDAA8BAQEBAQEBAQEBAQMDAQEBAQMDAQEBAQP5AwMDAAAACAAAAgAOABAAALwA3AAATNTsBHQE7AT0BOwEdASsBHQE7AR0BOwEdASsBHQEpAT0BKwE9ATsBPQE7AT0BKwEBNSsBHQE7AYBAQMDAQEBAQEBAQEBAQP7A/sBAQEBAQEBAQAIAwMDAwAPAQEBAQEBAQEBAQEDAwEBAQEDAwEBAQED+QMDAwAAAAgAAAIADgAQAADMAOwAAATU7AR0BKwEdATsBPQE7AT0BOwEdASsBHQE7AR0BOwEdASsBHQEpAT0BKwE9ATsBPQE7AQE1KwEdATsBAQBAQEBAgIBAQEBAQEBAQEBAQED+wP7AQEBAQEBAAYDAwMDAA8BAQEBAQEBAQEBAQEBAQEDAwEBAQEDAwICA/kDAwMAAAgAAAIADgAQAADMAOwAAATU7AR0BKwEdATsBPQE7AT0BOwEdASsBHQE7AR0BOwEdASsBHQEpAT0BKwE9ATsBPQE7AQE1KwEdATsBAQBAQEBAgIBAQEBAQEBAQEBAQED+wP7AQEBAQEBAAYDAwMDAA8BAQEBAQEBAQEBAQEBAQEDAwEBAQEDAwICA/kDAwMAAAgAAAIADgAQAAB8AJwAAEzUpAR0BKwEdATsBHQErAR0BOwEdASkBPQErARkBOwEBESsBGQE7AYABgAGAgICAgICAgID+gP6AQEBAQAEAQEBAQAPAQEBAgIBAQICAQEBAQAFAAUD+wAFA/sD+wAAAAAMAAACAA4ADAAAbACMAKwAAEzUpAR0BOwEdASsBHQE7AR0BKQE9ASsBPQE7AQU1KwEdATsBATUrAR0BOwGAAUABQEBAwMCAgP7A/sBAQEBAAQBAQEBAAQBAQEBAAsBAQECAgEBAQEBAQMDAwMDAwAFAQEBAAAAAAAIAAACAA4AEAAAzADsAAAE1OwEdASsBHQE7AR0BOwEdASsBHQE7AR0BOwEdASsBPQErAT0BKwEdASsBGQE7AT0BOwERNSsBHQE7AQIAQEBAQICAQECAgEBAQEDAwEBAQECAgMDAQECAgICAA8BAQEBAQEBAQEBAQEBAQEBAQEBAgIABQAFAQED+wEBAQAAAAAIAgACAA4AEAAAfACcAAAE1OwEdASsBHQE7AR0BKwEdASsBHQErARkBOwE9ATsBFTUrAR0BOwECAEBAQEDAwMDAQECAgICAQEBAQEBAA8BAQEBAQEBAQEDAwAFAAUBAQMBAQEAAAAAAAgAAAAADgAQAADMAPwAAGQEpAR0BOwEdASsBHQE7AR0BOwEdASsBHQErAR0BKwE9ATsBPQE7AT0BKwE9ASsBHQErAQE1KwEdATsBPQE7AQGAAYBAQICAQEBAQICAQECAgEBAQEBAQEBAgIACgMDAgIBAQAKAAYBAQICAQEBAQEBAQEBAQEBAQEBAQEBAgIACQECAgEBAAAABAIAAAAOAA4AAJwAAExE7AR0BOwE9ATsBHQErAR0BKwEdATsBHQErAR0BKwE9ATsBPQErAYCAgEBAwMDAwEBAQEBAQICAQEBAQAJAAUBAQEBAQEBAQMDAQEBAQEBAQEAAAAAAAgAAAIADgAQAADsAQwAAATU7AR0BOwE9ATsBHQErAR0BOwEdATsBHQErAR0BOwEdATsBHQErAT0BKwE9ASsBHQErARkBOwE9ASsBATUrAR0BOwEBAEBAQEBAQEBAgIBAQICAQEBAQMDAQEBAQICAwMBAQAEAgICAgAPAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQECAgAFAAUBAQP7AQEBAAAIAgACAA4AEAAAnAC8AAAE1OwEdATsBPQE7AR0BKwEdATsBHQErAR0BKwEdASsBGQE7AT0BKwEFNSsBHQE7AQEAgIBAQICAQEBAQMDAQECAgICAQEABAEBAQEADwEBAQEBAQEBAQEBAQEDAwAFAAUBAQMBAQEAAAAEAAACAA4AEAAA3AAABNTsBHQErAR0BOwEdASkBHQEpAR0BOwEdASsBHQEpAT0BKQE9ASkBPQErAT0BOwE9ATsBPQE7AQIAQEBAQICA/wD/AAEAAQBAQEBA/oD+gAFAAUD/AP8AQEBAQICAQEADwEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAQAAAIADgAQAADcAAAE1OwEdASsBHQE7AR0BKQEdASkBHQE7AR0BKwEdASkBPQEpAT0BKQE9ASsBPQE7AT0BOwE9ATsBAgBAQEBAgID/AP8AAQABAEBAQED+gP6AAUABQP8A/wBAQEBAgIBAQAPAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAACAAAAgAOABAAALwA3AAABNTsBHQE7AR0BKQEdASkBHQE7AR0BKwEdASkBPQEpAT0BKQE9ASsBPQE7AT0BOwEFNSsBHQE7AQEAwMBAQP8A/wABAAEAQEBAQP6A/oABQAFA/wD/AEBAQEBAQAEAQEBAQAPAQEBAgIBAQEBAQEBAQEBAQEBAQEBAgIBAQEBAAAACAAAAgAOABAAALwA3AAABNTsBHQE7AR0BKQEdASkBHQE7AR0BKwEdASkBPQEpAT0BKQE9ASsBPQE7AT0BOwEFNSsBHQE7AQEAwMBAQP8A/wABAAEAQEBAQP6A/oABQAFA/wD/AEBAQEBAQAEAQEBAQAPAQEBAgIBAQEBAQEBAQEBAQEBAQEBAgIBAQEBAAAABAAAAAAOABAAAPwAAEzUpAR0BKQEdASkBHQE7AR0BKwEdASsBHQErAR0BKwE9ATsBPQErAT0BKwE9ATsBHQE7AT0BKQE9ASsBPQE7AYABQAFA/wD/AAEAAQBAQEBAQEBAQICAgIDAwEBAgIDAwP8A/wBAQEBAA8BAQEBAQEBAgIBAQEBAQEBAQEBAQEBAQEBAgIBAQEBAAAEAAAAAA4ADgAA3AAATNSkBHQEpAR0BKQEdATsBHQErAR0BKwEdASsBHQErAT0BOwE9ASkBPQEpAT0BKQE9ASsBPQE7AYABQAFA/wD/AAEAAQBAQEBAQEBAQICAgID/AP8AAUABQP8A/wBAQEBAA0BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAQAAAIADgAQAAD8AABM1OwEdATsBPQE7AR0BKwEdATsBHQEpAR0BKQEdATsBHQErAR0BKQE9ASkBPQEpAT0BKwE9ATsBPQE7AT0BKwGAgIBAQICAQEBAQP8A/wABAAEAQEBAQP6A/oABQAFA/wD/AEBAQEBAQEBAA8BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAQAAAIADgAQAAD8AABM1OwEdATsBPQE7AR0BKwEdATsBHQEpAR0BKQEdATsBHQErAR0BKQE9ASkBPQEpAT0BKwE9ATsBPQE7AT0BKwGAgIBAQICAQEBAQP8A/wABAAEAQEBAQP6A/oABQAFA/wD/AEBAQEBAQEBAA8BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAQCAAAADgAQAABsAABM1KQEdASsBGQErAR0BKwE9ATsBPQErARkBKwGAAYABgICAQECAgICAQECAgAPAQEBA/oD+gEBAQEBAQAFAAUAAAAABAIAAAAOABAAAIwAAATU7AR0BOwEdASsBGQErAR0BKwE9ATsBPQErAT0BKwE9ATsBAYCAgICAgIBAQICAgIBAQICAgIADgICAgEBA/wD/AEBAQEBAQMDAQEAAAAEAgACAA4AEAAAnAAABNTsBHQE7AT0BOwEdASsBHQE7AR0BKwEZASsBGQErAT0BOwE9ASsBAQBAQICAQEBAQICAgICAgICAgIBAQAPAQEBAQEBAQEBAQED/AP8AAQABAEBAQEAAAAAAAQCAAIADgAQAACcAAAE1OwEdATsBPQE7AR0BKwEdATsBHQErAR0BKwE9ASsBPQE7AT0BKwEBAEBAgIBAQEBAgICAgICAgICAgEBAA8BAQEBAQEBAgIBAQMDAwMBAQICAAAAAAAEAgACAA4AEAAAfAAATNSkBHQErAR0BOwEdASsBHQErAT0BKwE9ATsBPQErAYABgAGAgIBAQEBAgIBAQEBAgIADwEBAQICAQEDAwICAQEDAwAAAAAEAgACAA4AEAAAnAAABNTsBHQE7AR0BKwEdATsBHQErAR0BKwE9ASsBPQE7AT0BKwE9ATsBAYCAgICAgIBAQEBAgIBAQEBAgICAgAOAgICAQEBAQEBAgIBAQEBAgIBAQAAAAAACAAAAgAOABAAAJwAzAAABNTsBHQE7AT0BOwEdASsBHQE7ARkBKwEdASkBPQErARkBOwE9ATsBFzUrARkBOwEZASsBAQCAgEBAQEBAQICAQED+wP7AQEBAQEBAgEBAwMCAgAPAQEBAQEBAQEBA/wD/AEBAQEABAAEAQEBAQP7A/sABAAEAAAAAAgAAAIADgAQAACMALwAAATU7AR0BOwE9ATsBHQErAR0BOwEZASkBPQErARkBOwE9ATsBFzUrARkBOwEZASsBAQCAgEBAQEBAQICA/oD+gEBAQEBAQIBAQMDAgIADwEBAQEBAQEBAQP7A/sBAQAEAAQBAQEBA/sD+wAEAAQAAAAACAAAAgAOABAAAFwAfAAAZATsBGQE7ARkBOwEZASsBHQEpAT0BKwETNSkBHQEpAYCAwMCAgEBA/sD+wEBAgAFAAUD+wP7AAgABAP8A/wABAAEA/wD/AEBAQEACwEBAQAACAAAAgAOABAAAEwAbAAAZATsBGQE7ARkBOwEZASkBPQErARM1KQEdASkBgIDAwICA/oD+gEBAgAFAAUD+wP7AAgABAP8A/wABAAEA/sD+wEBAAsBAQEAAAgAAAIADgAQAACcALwAAEzU7AR0BOwE9ATsBHQErAR0BOwEZASsBHQEpAT0BKwEZATsBPQErAQERKwEZATsBgEBAwMBAQEBAgIBAQP7A/sBAQICAQEACAMDAwMADwEBAQEBAQEBAQP8A/wBAQEBAAQABAEBA/oABAP8A/wAAAAACAAAAgAOABAAAIwArAAATNTsBHQE7AT0BOwEdASsBHQE7ARkBKQE9ASsBGQE7AT0BKwEBESsBGQE7AYBAQMDAQEBAQICA/oD+gEBAgIBAQAIAwMDAwAPAQEBAQEBAQEBA/sD+wEBAAQABAEBA/oABAP8A/wAAAAADAAAAgAOABAAAHwAnAD8AAAE1OwEdATsBHQE7ARkBKwEdASkBPQErARkBOwE9ATsBBTUrAR0BOwEFNSsBHQE7AR0BOwE9ATsBPQErAR0BKwEBAMDAQEBAQEBA/sD+wEBAQEBAQAEAQEBAQP8AQEBAQMDAQEBAQMDAA8BAQEBAQP8A/wBAQEBAAQABAEBAQEBAQEBAQEDAwMDAQEBAQAAAAwAAAIADgAQAABsAIwA7AAABNTsBHQE7AR0BOwEZASkBPQErARkBOwE9ATsBBTUrAR0BOwEFNSsBHQE7AR0BOwE9ATsBPQErAR0BKwEBAMDAQEBAQP6A/oBAQEBAQEABAEBAQED/AEBAQEDAwEBAQEDAwAPAQEBAQED+wP7AQEABAAEAQEBAQEBAQEBAQMDAwMBAQEBAAAABAAAAgAOABAAAMwAAATU7AR0BKwEZATsBGQErAT0BOwE9ATsBHQErAR0BOwEZASsBHQEpAT0BKwEZATsBPQE7AQEAQEBAQMDAQEBAQEBAQECAgEBA/sD+wEBAQEBAQAPAQEBA/sD+wAEAAQBAQEBAQEBAQP8A/wBAQEBAAQABAEBAAAABAAAAgAOABAAALwAAATU7AR0BKwEZATsBGQErAT0BOwE9ATsBHQErAR0BOwEZASkBPQErARkBOwE9ATsBAQBAQEBAwMBAQEBAQEBAQICA/oD+gEBAQEBAQAPAQEBA/sD+wAEAAQBAQEBAQEBAQP7A/sBAQAEAAQBAQAAAAQAAAAADgAQAAC8AABkBOwEZATsBGQE7ARkBKwEdASsBHQErAR0BOwEdASsBPQErAT0BOwE9ASsBPQErAYCAwMCAgEBAgIBAQICAgIBAQEBAgIBAQAMAAQD/AP8AAQABAP8A/wBAQEBAQEBAQEBAQEBAQEBAAAAAAQAAAAADgAQAACsAABkBOwEZATsBGQE7ARkBKwEdASsBHQE7AR0BKwE9ASsBPQE7AT0BKwE9ASsBgIDAwICAwMBAQICAgIBAQEBAgIBAQAMAAQD/AP8AAQABAP7A/sBAQEBAQEBAQEBAQEBAQAAAAAIAAACAA4AEAAAfADcAAAE1OwEdATsBHQE7ARkBKwE9ASsBHQErARkBOwE9ATsBBTUrAR0BKwEdATsBPQE7AR0BOwE9ASsBAQDAwEBAQEDAwEBAwMBAQEBAAQBAQEBAQEBAQEBAQEADwEBAQEBA/sD+wEBAQEABQAFAQEBAQEBAgIBAQEBAgIAAAAAAAwAAAIADgAQAABsAKwAzAAABNTsBHQE7AR0BOwEZASkBPQErARkBOwE9ATsBBTUrAR0BOwEZATsBGQErAQERKwEZATsBAQDAwEBAQED+gP6AQEBAQEBAAQBAQEBAQEBAQP8AQEBAQAPAQEBAQED+wP7AQEABAAEAQEBAQEBA/wD/AAEAAQD/AAEA/wD/AAAAAgCAAIADgAQAACcALwAAATU7AR0BOwEdATsBHQErAR0BKwEdASsBPQErAT0BKwE9ATsBPQE7AQU1KwEdATsBAYCAgEBAQEBAQEBAgIBAQEBAQEBAQAEAgICAgAPAQEBAQECAgEBAgICAgEBAgIBAQMDAwMAAAAAAAgAAAAADgAQAACcANwAAATU7AR0BOwEdATsBGQErAR0BKQE9ASkBPQEpAT0BKwE9ATsBPQE7AQU1KwEdASsBHQE7AT0BKwEBAMDAQEBAQEBA/sD+wAEAAQD/AP8AQEBAQEBAAQBAQEBAwMBAQAPAQEBAQED+wP7AQEBAQEBAQEDAwEBAQEBAQMDAwMAAAAAAAwCAAIADgAQAAB8AJwAvAAATNTsBHQE7AT0BOwEdASsBHQErAR0BKwE9ASsBPQErAQE1OwEdASsBJTU7AR0BKwGAgICAgICAQEBAQICAQEBAQAIAgICAgP4AgICAgAKAgICAgICAgEBAgICAgEBAAcBAQEBAQEBAAAAAAAEAAACAA4AEAAA3AAABNTsBHQErAR0BOwEdASsBHQErAR0BKwEdATsBHQEpAT0BOwE9ATsBPQE7AT0BKwE9ATsBPQE7AQIAQEBAQMDAQEBAQEBAwMD+QP5AQEBAQEBAwMDAwEBAA8BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAEAAACAA4AEAAA3AAABNTsBHQErAR0BOwEdASsBHQErAR0BKwEdATsBHQEpAT0BOwE9ATsBPQE7AT0BKwE9ATsBPQE7AQIAQEBAQMDAQEBAQEBAwMD+QP5AQEBAQEBAwMDAwEBAA8BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAIAAACAA4AEAAAnAC8AABE1KQEdASsBHQErAR0BKwEdATsBHQEpAT0BOwE9ATsBPQE7AT0BKwEBNTsBHQErAQHAAcBAQEBAQEDAwP5A/kBAQEBAQEDAwAGAgICAgALAQEBAQEBAQEBAQEBAQEBAQEBAQAFAQEBAAAIAAACAA4AEAAAnAC8AABE1KQEdASsBHQErAR0BKwEdATsBHQEpAT0BOwE9ATsBPQE7AT0BKwEBNTsBHQErAQHAAcBAQEBAQEDAwP5A/kBAQEBAQEDAwAGAgICAgALAQEBAQEBAQEBAQEBAQEBAQEBAQAFAQEBAAAEAAACAA4AEAAA/AAATNTsBHQE7AT0BOwEdASsBHQE7AR0BKwEdASsBHQErAR0BOwEdASkBPQE7AT0BOwE9ATsBPQErAT0BOwE9ASsBgICAQECAgEBAgIBAQEBAQEDAwP5A/kBAQEBAQEDAwICAQEADwEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAABAAAAgAOABAAAPwAAEzU7AR0BOwE9ATsBHQErAR0BOwEdASsBHQErAR0BKwEdATsBHQEpAT0BOwE9ATsBPQE7AT0BKwE9ATsBPQErAYCAgEBAgIBAQICAQEBAQEBAwMD+QP5AQEBAQEBAwMCAgEBAA8BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAQGAAIADgAQAAA8AAAE1OwEdASsBGQErARkBOwECAMDAgICAgEBAA8BAQED+gP6AAYABgAAAAAABAIAAgAOABAAAJwAAATU7AR0BKwEdATsBHQErAR0BKwEdASsBPQE7AT0BKwE9ATsBPQE7AQIAwMCAgICAgIBAQMDAgICAgICAQEADwEBAQEBAQEDAwEBAQEDAwEBAQEAAAAAAAQCAAwADAAQAABcAAAE1OwEdATsBHQErAT0BKwEdASsBPQE7AQEAwMBAQICAQECAgEBAA8BAQEBAQEBAQEBAQAAAAAABAIADAAMABAAAFwAAEzU7AR0BOwE9ATsBHQErAR0BKwE9ASsBgICAQECAgEBAwMBAQAPAQEBAQEBAQEBAQEAAAQCAA4ADAAQAAAcAABM1KQEdASkBgAFAAUD+wP7AA8BAQEAAAQGAAwACgAQAAA8AAAE1OwEdASsBHQErAT0BOwECAEBAQEBAQEBAA8BAQEBAQEBAAAAAAAEBgAMAAoAEAAAPAAABNTsBHQE7AR0BKwE9ASsBAYBAQEBAQEBAQAPAQEBAQEBAQAAAAAABAIACAAMAAoAABwAAEzUpAR0BKQGAAUABQP7A/sACQEBAQAABAIADAAMABAAAFwAAEzU7AR0BOwE9ATsBHQErAR0BKwE9ASsBgEBAwMBAQEBAwMBAQAPAQEBAQEBAQEBAQEAAAQGAA4ACgAQAAAcAAAE1OwEdASsBAYCAgICAA8BAQEAAAAAAAgCAAoADAAQAABcAHwAAATU7AR0BOwEdASsBHQErAT0BKwE9ATsBBTUrAR0BOwEBAMDAQEBAQMDAQEBAQAEAQEBAQAPAQEBAQEBAQEBAQEBAQEBAAAAAAAEBAAAAAoABgAAXAAABNTsBHQErAR0BOwEdASsBPQErAT0BOwEBgEBAQECAgICAQEBAQAFAQEBAQEBAQEBAQEAAAAAAAQCAAwADAAQAAB8AAAE1OwEdATsBPQE7AR0BKwEdASsBPQErAR0BKwE9ATsBAQCAgEBAQEBAQICAQEBAQEBAA8BAQEBAQEBAQEBAQEBAQEAAAAAAAgCAAwADAAQAAA8AHwAAATU7AR0BKwEdASsBPQE7ASU1OwEdASsBHQErAT0BOwECgEBAQEBAQEBA/oBAQEBAQEBAQAPAQEBAQEBAQEBAQEBAQEBAAAAAAAEBgAAAAwABAAAPAAAlNTsBHQE7AR0BKwE9ASsBAYBAQICAgIBAQMBAQEBAQEBAAAIAgACAAgADgAAPABcAAAE1OwEdASsBHQErAT0BOwERNTsBHQErAQEAgIBAQICAQECAgICAAYCAgIBAQEBAAgCAgIAAAQGAAwACgAQAAA8AAAE1OwEdASsBHQErAT0BOwECAEBAQEBAQEBAA8BAQEBAQEBAAAAAAAMAgAMAA4AEAAAHABcAHwAAATU7AR0BKwElNTsBHQErAR0BKwE9ATsBJTU7AR0BKwEDAEBAQED/AEBAQEBAQEBA/oBAQEBAA8BAQEBAQEBAQEBAQEBAQEAAAAAAAwAAAIADgAQAACcALwA3AAATNTsBHQE7AT0BOwEdATsBHQE7ARkBKwE9ASsBHQErARkBKwE9ATsBFzUrAR0BOwEFNSsBHQE7AYBAQEBAgIBAQEBAgICAgICAQEBAQIBAQEBAAYCAgICAA8BAQEBAQEBAQED+wP7AgICAgAFAAUBAQEBAQECAgICAAAABAQABgAIAAoAABwAAATU7AR0BKwEBAICAgIACAICAgAAAAAABAAAAgAQABAAAIwAAEzUpAR0BKQEdATsBHQErAR0BKQEdASkBGQErAR0BKwE9ATsBgAHAAcD/AP8AgICAgAEAAQD+gP6AQEBAQEBAA8BAQECAgEBAgIBAQAGAAYBAQEBAAAAAAQAAAIAEAAQAACMAABM1OwEdATsBPQE7ARkBKwE9ASsBHQErARkBKwEdASsBPQE7AYDAwICAgICAgICAgIBAQEBAQEADwEDAwMDA/kD+QMDAwMABgAGAQEBAQAACAAAAgAOABAAAFwAnAAABNSkBHQErARkBOwEdASkBPQE7ARkBKwElNTsBHQErAR0BKwE9ATsBAYABAAEAQEBAQP8A/wBAQEBA/wBAQEBAQEBAQAPAQEBA/sD+wEBAQEABQAFAQEBAQEBAQEAAAAAAAgAAAIAEAAQAACcALwAAEzU7AR0BOwE9ASkBHQE7ARkBKwEdASkBPQErARkBKwEdASsBPQE7AQERKwEZATsBgEBAQEABAAEAQEBAQP8A/wBAQEBAQEBAQAKAgICAgAPAQEBAQEBAQP7A/sBAQEBAAUABQEBAQED+wAFA/sD+wAABAAAAgAQABAAAKwAAEzU7AR0BOwE9ATsBHQErAR0BKwEdASsBPQErAT0BKwE9ASsBHQErAT0BOwGAwMCAgICAQEBAQICAQEBAQEBAQEBAQAPAQMDAwMDAwEBAwMDAwEBAgIBAQEBAAAIAAACABAAEAABHAE8AABM1OwEdATsBPQE7AR0BOwEdATsBHQErAR0BOwEdASsBPQE7AT0BKwE9ASsBHQErAR0BOwEdASsBPQE7AT0BKwE9ASsBPQE7ARc1KwEdATsBgEBAQEDAwEBAQEBAQEBAwMBAQEBAQEBAQEBAwMBAQEBAQEBAQIBAQEBAA8BAQEBAQEBAQEDAwEBAQECAgMDAQEBAQMDAgIBAQEBAwMBAQEBAQEAAAAMAAACAAwAEAAAHACsAMwAAATU7AR0BKwElNTsBHQErAR0BOwEZATsBHQErAT0BKwE9ASsBPQE7AT0BOwElNTsBHQErAQKAQEBAQP8AQEBAQEBAgIDAwEBAQEBAQEBA/oBAQEBAA8BAQEBAQEBAQED/AP8AQEBAQMDAQEBAQEBAQEAAAAIAAACAA4AEAAAfAC8AAAE1OwEdATsBHQE7ARkBKwE9ASsBHQErARkBOwE9ATsBBTUrAR0BKwEdATsBPQErAQEAwMBAQEBAgIDAwICAQEBAQAEAQEBAQMDAQEADwEBAQEBA/sD+wICAgIABQAFAQEBAQEBAgICAgAAAAAADAAAAgAOABAAAFwAfACcAABkBKQEdATsBHQErAR0BOwEdASsBHQEpAQE1KwEdATsBETUrAR0BOwEBgAGAQEBAQEBAQED+gP6AAoDAwMDAwMDAwAJAAcBAQICAQECAgEBAAoCAgID/AICAgAABAIAAgAOABAAACwAAExEpAR0BKQEZASsBgAGAAYD/AP8AgIACQAHAQED+gP6AAAACAAAAgAOABAAAFwAnAAABNTsBHQE7AR0BOwEZASkBGQE7AT0BOwEFNSsBHQErARkBOwEZASsBAQDAwEBAQED+QP5AQEBAQAEAQEBAQMDAQEADwEBAQEBA/sD+wAFAAUBAQEBAQED/AP8AAQABAAAAAQAAAIADgAQAABcAABkBKQEdASkBHQEpAR0BKQEdASkBHQEpAQHAAcD+wP7AAQABAP8A/wABQAFA/kD+QAJAAcBAQICAQECAgEBAAAEAAACAA4AEAAAvAAARNSkBHQErAR0BKwEdASsBHQErAR0BKQEdASkBPQE7AT0BOwE9ATsBPQE7AT0BKQEBwAHAQEBAQEBAQEABAAEA/kD+QEBAQEBAQEBA/wD/AAPAQICAQEBAQEBAQEBAQICAQEBAQEBAQEAAAAEAAACAA4AEAAAXAAAZATsBHQE7AT0BOwEZASsBPQErAR0BKwGAgMDAgICAgMDAgIACQAHAwMDAwP5A/kDAwMDAAAAAAwAAAIADgAQAABcAHwAnAAATNSkBHQE7ARkBKwEdASkBPQErARkBOwEFNSsBHQE7ARE1KwEdATsBgAFAAUBAQEBA/sD+wEBAQEACAMDAwMDAwMDAA8BAQED+wP7AQEBAQAFAAUCAgICA/wCAgIAAAAEAgACAA4AEAAAXAAATNSkBHQErARkBOwEdASkBPQE7ARkBKwGAAYABgICAgID+gP6AgICAgAPAQEBA/sD+wEBAQEABQAFAAAEAAACAA4AEAAA7AAAZATsBHQE7AT0BOwE9ATsBPQE7AR0BKwEdASsBHQErAR0BOwEdATsBHQE7AR0BKwE9ASsBPQErAR0BKwGAgEBAQEBAQICAQEBAQEBAQEBAQEBAwMBAQEBAgIACQAHAwMBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQECAgAABAAAAgAOABAAAJwAAATU7AR0BOwEdATsBGQErARkBKwE9ASsBHQErARkBKwEZATsBPQE7AQEAwMBAQEBAgIBAQEBAQECAgEBAQEADwEBAQEBA/sD+wAFAAUBAQEBA/sD+wAFAAUBAQAAAAAABAAAAgAOABAAAJwAAGQE7AR0BOwEdATsBPQE7AT0BOwEZASsBPQErAR0BKwE9ASsBHQErAYCAQEBAQEBAgICAgEBAQEBAQICAAkABwEBAQEBAQEBA/kD+QMDAQEBAQMDAAAAAAQAAAIADgAQAACcAABkBOwEdATsBHQE7AR0BOwE9ATsBGQErAT0BKwE9ASsBPQErAR0BKwGAgEBAQEBAQICAgIBAQEBAQECAgAJAAcBAQEBAQEDAwP5A/kBAQEBAQEDAwAAAAAMAAACAA4AEAAAHAA8AFwAAPQEpAR0BKQETNSkBHQEpAQM1KQEdASkBAcABwP5A/kCAAUABQP7A/sCAAcABwP5A/kDAQEBAAcBAQEABwEBAQAAAAAIAAACAA4AEAAAXAB8AABM1KQEdATsBGQErAR0BKQE9ASsBGQE7AQERKwEZATsBgAFAAUBAQEBA/sD+wEBAQEACAMDAwMADwEBAQP7A/sBAQEBAAUABQP7AAUD+wP7AAAEAAACAA4AEAAAPAAAZASkBGQErARkBKwEZASsBAcABwICAwMCAgAJAAcD+QP5AAYABgP6A/oAAAgAAAIADgAQAABMAGwAAGQEpAR0BOwEdASsBHQEpAR0BKwEBNSsBHQE7AQGAAYBAQEBA/wD/AICAAoDAwMDAAkABwEBAwMBAQICAAkDAwMAAAAAAAQAAAIADgAQAAD8AABE1KQEdASsBPQErAR0BOwEdATsBHQErAR0BKwEdATsBPQE7AR0BKQE9ATsBPQE7AT0BOwE9ASsBPQErAT0BKwEBwAHAgICAgEBAgICAgEBAgICAgP5A/kBAQEBAQEBAQEBAQEADwECAgEBAQEBAQEBAQEBAQEBAgIBAQEBAQEBAQEBAQEAAAAEAgACAA4AEAAAPAAATNSkBHQErARkBKwEZASsBgAGAAYCAgICAgIADwEBAQP6A/oABgAGAAAAAAQCAAIADgAQAAB8AABM1OwEdATsBPQE7AR0BKwEdASsBHQErAT0BKwE9ASsBgICAgICAgEBAQECAgEBAQEADQMDAwMDAwMBAQMDAwMBAQAADAAAAgAOABAAAJwAvADcAAAE1OwEdATsBHQE7AR0BKwEdASsBHQErAT0BKwE9ASsBPQE7AT0BOwERNSsBHQE7ASU1KwEdATsBAYBAQICAQEBAQICAQECAgEBAQECAgEBAQEABAEBAQEADwEBAQEBAwMBAQEBAQEBAQMDAQED+wMDAwMDAwMAAAQAAAIADgAQAAEcAABE1OwEdATsBHQE7AT0BOwE9ATsBHQErAR0BKwEdATsBHQE7AR0BKwE9ASsBPQErAR0BKwEdASsBPQE7AT0BOwE9ASsBPQErAYCAQEBAQEBAgIBAQEBAQEBAQICAQEBAQEBAgIBAQEBAQEBAQAOAgEBAQEBAQEBAgIBAQEBAQECAgEBAQEBAQEBAgIBAQEBAQEAAAAEAAACABAAEAAAvAAARNTsBHQE7AT0BOwEdATsBPQE7AR0BKwEdASsBHQE7AR0BKQE9ATsBPQErAT0BKwGAgEBAgIBAQICAQECAgEBA/wD/AEBAgIBAQANAwMDAwMDAwMDAwMBAQICAQEBAQICAQEAAAAAAAQAAAIADgAQAAD8AAAE1OwEdATsBHQE7AR0BKwEdATsBHQErAT0BOwE9ASsBPQErAR0BKwEdATsBHQErAT0BOwE9ASsBPQE7AT0BOwEBAMDAQEBAQEBAQEDAwEBAQEBAQEBAQEDAwEBAQEBAQEBAA8BAQEBAQMDAQEBAQICAwMBAQEBAwMCAgEBAQEDAwEBAAAAAAAMAgACAA4AEAAAXAB8AJwAAEzUpAR0BKwEdATsBHQEpAT0BOwE9ASsBATU7AR0BKwElNTsBHQErAYABgAGAgICAgP6A/oCAgICAAgCAgICA/gCAgICAAsBAQEDAwEBAQEDAwAFAQEBAQEBAQAAAAAADAIAAgAOABAAAHwAnAC8AABM1OwEdATsBPQE7AR0BKwEdASsBHQErAT0BKwE9ASsBATU7AR0BKwElNTsBHQErAYCAgICAgIBAQEBAgIBAQEBAAgCAgICA/gCAgICAAoCAgICAgICAQECAgICAQEABwEBAQEBAQEAAAAAAAgAAAIADgAQAAB8AJwAAATU7AR0BKwEdATsBGQEpAT0BKwE9ATsBPQE7AT0BOwETNSsBHQE7AQIAQEBAQMDA/oD+gEBAQECAgEBAgMDAwMADwEBAQEBA/sD+wEBAwMBAQEBA/kDAwMAAAAAAAQAAAIADgAQAADcAAAE1OwEdASsBHQE7AR0BKQEdASkBHQEpAR0BKQEdASkBPQErAT0BOwE9ASsBPQE7AT0BOwE9ATsBAgBAQEBAwMD+wP7AAQABAP8A/wABQAFA/oD+gEBAQEBAQEBAgIBAQAPAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAABAAAAAAOABAAAKwAAATU7AR0BKwEdATsBHQE7ARkBKwEZASsBGQErARkBKwE9ATsBHQE7AT0BOwECAEBAQECAgEBAgICAgICAQECAgEBAQEADwEBAQEBAQED+wP7AAUABQP8A/wABAAEAQEBAQICAAAAAAAEAgACAAwAEAAAjAAABNTsBHQErAR0BOwEZATsBHQErAT0BKwE9ASsBPQE7AT0BOwEBgEBAQEBAQICAwMBAQEBAQEBAQAPAQEBAQED/AP8AQEBAQMDAQEBAQAAAAwAAAIADgAQAAAcAPwBHAAABNTsBHQErASU1OwEdASsBHQErAR0BOwEdATsBPQErAT0BOwEdATsBHQErAR0BKQE9ASsBPQErAT0BOwE9ATsBJTU7AR0BKwECgEBAQED/AEBAQEBAQEBAgIBAQICAQEBAQP8A/wBAQEBAgIBAQP6AQEBAQAPAQEBAQEBAQEBAQEDAwMDAQEBAQMDAQEBAQMDAQEBAQEBAQEAAAAIAAACAA4ADAAAPABcAABM1KQEZASkBPQErAT0BOwEFNSsBHQE7AYABgAGA/oD+gEBAQEACAMDAwMACwED+wP7AQEDAwMDAwMAAAAADAAAAAAOABAAAHwAnAC8AABM1KQEdATsBHQErAR0BOwEdASsBHQEpAR0BKwEZATsBBTUrAR0BOwERNSsBHQE7AYABQAFAQEBAQEBAQED/AP8AgIBAQAIAwMDAwMDAwMADwEBAQICAQECAgEBAQEABwAHAgICAgP8AgICAAAAAAAEAAAAAA4ADAAAnAAARNTsBHQE7AR0BOwE9ATsBHQErAR0BKwEdASsBPQErAT0BKwE9ASsBgIBAQICAgIBAQEBAgIBAQEBAQEACwEBAQICAwMDAwEBAgICAgEBAgIAAAAIAAACAA4AEAAArADMAAAE1OwEdASsBHQE7AR0BOwEdASsBHQEpAT0BKwE9ATsBPQE7AT0BKwE9ATsBEzUrAR0BOwECAMDAgIBAQEBAQED+wP7AQEBAQMDAQEBAQIDAwMDAA8BAQEBAQEBAwMBAQEBAgIBAQEBAQED+AICAgAAAAQAAAIADgAMAACcAABM1KQEdASkBHQEpAR0BKQEdASkBHQEpAT0BKwE9ATsBPQErAT0BOwGAAYABgP7A/sABAAEA/wD/AAFAAUD+gP6AQEBAQEBAQEACwEBAQEBAQEBAQEBAQEBAQEBAQEAAAQAAAAADgAQAADcAABM1KQEdASsBHQErAR0BKwEdASkBHQE7AR0BKwEdASsBPQE7AT0BKQE9ASsBPQE7AT0BOwE9ASsBgAFAAUCAgEBAQEABAAEAQEBAQICAQED/AP8AQEBAQEBAQEADwEBAQEBAQECAgEBAQEBAQEBAQEBAQICAQEBAQAAAAAEAAAAAA4ADAAAfAAARNTsBHQE7AT0BOwEdATsBGQErARkBKwEZASsBGQErAYCAQEDAwEBAgICAgICAQEACwEBAQEBAQED+wP7AAUABQP8A/wABAAEAAAADAAAAgAOABAAAJwA3AEcAAAE1OwEdATsBHQE7AR0BKwEdASsBHQErAT0BKwE9ASsBPQE7AT0BOwEFNSsBHQErAR0BOwE9ASsBEzUrAR0BOwEdATsBPQE7AQEAwMBAQEBAQEBAQMDAQEBAQEBAQEABAEBAQEDAwEBAgMDAQEBAQEBAA8BAQEBAQMDAQEBAQEBAQEDAwEBAQEBAQEBAQED+wEBAQEBAQEAAAAAAAQCAAIADAAMAABMAABM1OwEZATsBHQErAT0BKwE9ASsBgMDAgIDAwEBAQEACwED/AP8AQEBAQMDAAAAAAQAAAIADgAMAAC8AABkBOwEdATsBPQE7AT0BOwEdASsBHQErAR0BOwEdATsBHQErAT0BKwE9ASsBHQErAYCAgIBAQICAQEBAQEBAQECAgEBAgICAgAHAAUCAgEBAQEBAQEBAQEBAQEBAQEBAQICAAAEAAACAA4AEAAA7AAARNTsBHQE7AR0BOwEdATsBHQE7AR0BOwEdASsBPQErAT0BKwEdASsBHQErAT0BOwE9ATsBPQErAT0BKwGAgEBAQEBAQEBAQECAgEBAQEBAQICAQEBAQEBAQEADwEBAQEBAQEBAQEBAgICAgEBAQECAgICAQECAgEBAAAABAAAAAAOAAwAAHwAAGQE7ARkBOwEZATsBGQE7AR0BKwE9ASsBHQErAR0BKwGAgICAgIBAQICAQECAgICAAYABgP8A/wABAAEA/wD/AEBAQEBAQEBAAAAAAQAAAIADgAMAACcAABE1OwEdATsBHQE7AT0BOwEdASsBHQErAR0BKwE9ASsBPQErAT0BKwGAgEBAgICAgEBAQECAgEBAQEBAQALAQEBAgIDAwMDAQEBAQEBAQECAgAAAAQAAAAADAAQAAD8AABE1KQEdASsBHQE7AR0BKwEdASsBHQE7AR0BOwEdASsBHQErAT0BOwE9ASsBPQErAT0BOwE9ATsBPQErAT0BKwEBgAGAwMCAgICAQEDAwEBAQECAgEBAwMBAQEBAQEBAQEBAA8BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAIAAACAA4ADAAAXAB8AABM1KQEdATsBHQErAR0BKQE9ASsBPQE7AQU1KwEdATsBgAFAAUBAQEBA/sD+wEBAQEACAMDAwMACwEBAQMDAQEBAQMDAwMDAwAABAAAAgAOAAwAAIwAAETUpAR0BKwEdATsBHQErAT0BKwE9ASsBGQErAT0BOwE9ASsBAcABwEBAQECAgEBAQEDAwEBAQEACwEBAQMDAQEBAQMDA/wD/AEBAwMAAAAIAAAAAA4ADAAAXAB8AABM1KQEdATsBHQErAR0BKQEdASsBGQE7AQU1KwEdATsBgAFAAUBAQEBA/wD/AICAQEACAMDAwMACwEBAQICAQECAgAFAAUCAgICAAAAAAQAAAAADgAMAACcAABM1KQEdASkBHQEpAR0BOwEdASsBHQEpAT0BKQE9ASkBPQErAT0BOwGAAUABQP8A/wABAAEAQEBAQP7A/sABAAEA/wD/AEBAQEACwEBAQICAQEBAQEBAQEBAQEBAgIAAAgAAAIADgAMAABsAJwAAEzUpAR0BKwEdATsBHQErAR0BKQE9ASsBPQE7AQU1KwEdATsBPQErAYABgAGAgIBAQEBA/wD/AEBAQEABAEBAgIBAQALAQEBAQECAgEBAQEDAwEBAwMCAgAABAIAAgAOAAwAAFwAAEzUpAR0BKwEdATsBHQErAT0BKwE9ASsBgAGAAYCAgICAwMBAQICAAsBAQEDAwEBAQEDAwAAAAAEAAACAA4ADAAAnAAARNTsBHQE7AR0BOwE9ASsBPQE7AR0BOwEdASsBHQEpAT0BKwE9ASsBgIBAQICAQECAgEBAQED/AP8AQEBAQALAQEBAwMDAwEBAQEDAwEBAQEDAwAAAAAACAAAAAAOAAwAAKwAzAAATNTsBGQE7AT0BOwE9ATsBHQE7AR0BKwEdASsBHQErAT0BKwE9ASsBPQE7AQU1KwEdATsBgEBAQEBAQICAQEBAQICAQECAgEBAQEACAEBAQEACwED/AP8AwMBAQEBAwMBAQEBAQEBAQMDAwMDAwAAAAAEAAAAAA4ADAABHAAARNTsBHQE7AR0BOwE9ATsBPQE7AR0BKwEdASsBHQE7AR0BOwEdASsBPQErAT0BKwEdASsBHQErAT0BOwE9ATsBPQErAT0BKwGAgEBAQEBAQICAQEBAQEBAQECAgEBAQEBAQICAQEBAQEBAQEACwEBAQEBAQEBAQEBAQEBAQEBAgICAgEBAQECAgICAQEBAQEBAAAABAAAAAAQAA4AAJwAAARE7ARkBOwEZATsBGQErAR0BKwEdASsBPQErAT0BKwEZATsBGQE7AQGAgIBAQICAQECAgICAgIBAQICAQEACQAFA/sD+wAEAAQD/AP8AQEBAQEBAQEABAAEA/wD/AAABAAAAgAOAAwAALwAAEzU7ARkBOwE9ATsBHQE7ARkBOwEdATsBHQErAR0BKwE9ASsBHQErAT0BKwE9ATsBgEBAQEBAQEBAQEBAQEBAgIBAQICAQEBAQALAQP8A/wDAwMDAAQABAEBAwMBAQEBAQEBAQMDAAAMAAACAAwAEAAATABsAIwAAEzU7ARkBOwEdASsBPQErAT0BKwEBNTsBHQErASU1OwEdASsBgMDAgIDAwEBAQEABAICAgID+gICAgIACwED/AP8AQEBAQMDAAUBAQEBAQEBAAAADAAAAgAOABAAAJwAvADcAABE1OwEdATsBHQE7AT0BKwE9ATsBHQE7AR0BKwEdASkBPQErAT0BKwEBNTsBHQErASU1OwEdASsBgIBAQICAQECAgEBAQED/AP8AQEBAQAIAgICAgP6AgICAgALAQEBAwMDAwEBAQEDAwEBAQEDAwAFAQEBAQEBAQAAAAAIAAACAA4AEAAAnAC8AAAE1OwEdASsBHQE7AR0BOwEdASsBHQEpAT0BKwE9ATsBPQE7AT0BOwETNSsBHQE7AQIAQEBAQICAQEBAQP7A/sBAQEBAgIBAQIDAwMDAA8BAQEBAQEBAwMBAQEBAwMBAQEBA/kDAwMAAAAEAAACAA4AEAAA3AAABNTsBHQErAR0BOwEdATsBHQErAR0BKQE9ASsBPQErAT0BOwEdATsBHQE7AT0BKwE9ASsBPQE7AQIAQEBAQICAQEBAQP8A/wBAQEBAgIBAQICAQEBAQEBAA8BAQEBAQEBAwMBAQEBAwMBAQEBAwMDAwEBAQEAAAAIAAACAA4AEAAAvAD8AABM1OwEZATsBPQE7AR0BOwEZATsBHQE7AR0BKwEdASsBPQErAR0BKwE9ASsBPQE7AQE1OwEdASsBHQErAT0BOwGAQEBAQEBAQEBAQEBAQECAgEBAgIBAQEBAAYBAQEBAQEBAQALAQP8A/wDAwMDAAQABAEBAwMBAQEBAQEBAQMDAAUBAQEBAQEBAAAAAAAEAAACAA4AEAAAnAAABNTsBHQE7AR0BOwEdASkBHQEpAR0BKQEdASkBHQEpARkBOwE9ASsBAQBAQEBAwMD+wP7AAQABAP8A/wABQAFA/kD+QMDAQEADwEBAQEBAQEBAQEBAQEBAQAFAAUBAQAAAAAADAAAAgAOABAAAFwAfACcAABkBKQEdASkBHQEpAR0BKQEdASkBHQEpAQE1OwEdASsBJTU7AR0BKwEBwAHA/sD+wAEAAQD/AP8AAUABQP5A/kACAICAgID+gICAgIABwAFAQEBAQEBAQEBAQANAQEBAQEBAQAAAAAABAAAAAAOABAAAJwAAETUpAR0BKwEdATsBHQE7ARkBKwEdASsBPQE7ARkBKwEZASsBGQErAQFAAUCAgMDAQEBAQICAQECAgICAQEADwEBAQEBAQED/AP8AQEBAQAEAAQD/AP8AAYABgAAAAAABAIAAgAOABAAAGwAAATU7AR0BKwEdATsBHQEpARkBKwEZATsBPQE7AQIAQEBAQMDA/wD/AICAgIBAQAPAQEBAQEBAQP8A/wABQAFAQEAAAAEAAACAA4AEAAA/AAABNSkBHQE7AR0BKwE9ASsBHQErAR0BKQEdASkBHQE7AR0BOwE9ATsBHQErAR0BKQE9ASsBPQErAT0BOwE9ATsBAQABAAEAQECAgICAQEABAAEA/wD/AEBAgICAgEBA/wD/AEBAQEBAQEBAA8BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEDAwEBAAAAAAAEAAACAA4AEAAA3AAATNSkBHQE7AR0BKwE9ASsBHQEpAR0BOwEdASsBHQEpAT0BKwE9ATsBHQE7AT0BKQE9ASsBPQE7AYABAAEAQECAgICAAQABAEBAQED+wP7AQECAgMDA/wD/AEBAQEADwEBAQEBAQECAgEBAgIBAQEBAQEBAQICAQECAgAABAIAAgAOABAAAFwAAEzUpAR0BKwEZATsBHQEpAT0BOwEZASsBgAGAAYCAgICA/oD+gICAgIADwEBAQP7A/sBAQEBAAUABQAADAIAAgAOABAAAFwAfACcAABM1KQEdASsBHQE7AR0BKQE9ATsBPQErAQE1OwEdASsBJTU7AR0BKwGAAYABgICAgID+gP6AgICAgAIAgICAgP4AgICAgALAQEBAwMBAQEBAwMABQEBAQEBAQEAAAAAAAQAAAIADgAQAABcAAAEROwEZASsBHQEpAT0BKwE9ATsBHQE7AQKAgIBAQP7A/sBAQICAwMACgAGA/oD+gEBAQEBAQEBAAAAAAgAAAIADgAQAACMAKwAAATU7AR0BOwEdATsBHQErAR0BKwEZASsBGQErARkBOwE9ATsBATUrAR0BOwEBAICAgIBAQEBAwMBAQICAQEBAQAGAQEBAQAPAQICAQEDAwEBAAUABQP7A/sABQAFAQED+QMDAwAACAAAAgAOABAAAIwArAAAZATsBHQE7AT0BOwEdATsBHQE7AR0BKwEdASsBPQErAR0BKwEBNSsBHQE7AYCAQEBAQICAQEBAQMDAQECAgAKAQEBAQAJAAcDAwMDAgIBAQMDAQEDAwMDAAUDAwMAAAAAAAQAAAIADgAQAAB8AABE1KQEdASsBHQE7AR0BOwEZASsBGQErARkBKwEZASsBAUABQICAwMBAQICAgICAgEBAA8BAQEBAQEBA/wD/AAEAAQD/AP8AAYABgAAAAAACAAAAgAOABAAALwA/AAAZATsBHQE7AT0BOwE9ATsBHQErAR0BKwEdATsBHQE7AR0BKwE9ASsBPQErAR0BKwEBNTsBHQErAR0BKwE9ATsBgICAgEBAgIBAQEBAQEBAQICAQECAgICAAgBAQEBAQEBAQAHAAUCAgEBAQEBAQEBAQEBAQEBAQEBAQICAA0BAQEBAQEBAAAAAAAEAAACAA4AEAAAvAAABNTsBHQE7AR0BOwEZASsBPQErAR0BKwEdASsBGQE7AR0BOwE9ATsBPQErAT0BKwEBAEBAQEDAwICAQEBAQMDAgIBAQEBAQEBAQAPAQEBAQED+wP7AgIBAQEBAAUABQICAQEBAQEBAAAAAAAIAAACAA4AEAAAvADcAABM1OwEdATsBPQE7AR0BKwEdATsBGQErAR0BKQE9ASkBPQEpAT0BKwE9ATsBPQErAQE1KwEdATsBgEBAwMBAQEBAgIBAQP6A/oABQAFA/wD/AEBAgIBAQAIAwMDAwAPAQEBAQEBAQEBA/wD/AEBAQEBAQEBAgIBAQP8AgICAAAAAAAEAAAAAA4AEAAAXAAAZATsBGQE7ARkBOwEZASsBHQErAT0BKwGAgMDAgIDAwEBAwMACQAHA/oD+gAGAAYD+QP5AQEBAQAAAAAIAAACAA4AEAAAfAC8AAAE1OwEdATsBHQE7ARkBKwE9ASsBHQErARkBOwE9ATsBBTUrAR0BKwEdATsBPQErAQEAwMBAQEBAgIDAwICAQEBAQAEAQEBAQMDAQEADwEBAQEBA/sD+wICAgIABQAFAQEBAQEBAgICAgAAAAAACAAAAgAOABAAAFwAfAAAZASkBHQEpAR0BKQEdATsBHQErAR0BKQEBNSsBHQE7AQGAAYD/AP8AAQABAEBAQED+gP6AAoDAwMDAAkABwEBAgIBAQICAQEABAICAgAAAAAADAAAAgAOABAAAFwAfACcAABkBKQEdATsBHQErAR0BOwEdASsBHQEpAQE1KwEdATsBETUrAR0BOwEBgAGAQEBAQEBAQED+gP6AAoDAwMDAwMDAwAJAAcBAQICAQECAgEBAAoCAgID/AICAgAABAIAAgAOABAAACwAAExEpAR0BKQEZASsBgAGAAYD/AP8AgIACQAHAQED+gP6AAAACAAAAAAQABAAAHwArAAABNSkBGQE7AR0BKwE9ASkBHQErAT0BOwE9ATsBPQE7AQERKwEdASsBHQE7AQGAAQABAEBAgID/AP8AgIBAQEBAQEABAEBAQECAgAPAQP5A/kBAQEBAQECAgMDAgID+wAFAgIDAwAAAAAABAAAAgAOABAAAFwAAGQEpAR0BKQEdASkBHQEpAR0BKQEdASkBAcABwP7A/sABAAEA/wD/AAFAAUD+QP5AAkABwEBAgIBAQICAQEAAAQAAAIADgAQAAEcAABE1OwEdATsBPQE7AR0BOwE9ATsBHQErAR0BKwEdATsBHQE7AR0BKwE9ASsBHQErAT0BKwEdASsBPQE7AT0BOwE9ASsBPQErAYCAQEBAQEBAgIBAQEBAQEBAQICAQEBAQEBAgIBAQEBAQEBAQAOAgICAgICAgICAgIBAQEBAQECAgICAgICAgICAgIBAQEBAQEAAAAEAAACAA4AEAAA3AAATNSkBHQE7AR0BKwEdATsBHQErAR0BKQE9ASsBPQE7AR0BOwE9ASsBPQE7AT0BKwEdASsBPQE7AYABQAFAQEBAQEBAQED+wP7AQECAgMDAwMDAwMDAgIBAQAPAQEBAgIBAQICAQEBAQEBAQECAgEBAgIBAQEBAAAEAAACAA4AEAAAnAAAZATsBHQE7AT0BOwE9ATsBPQE7ARkBKwE9ASsBHQErAR0BKwEdASsBgIBAQEBAQECAgICAQEBAQEBAgIACQAHAwMBAQEBAQED+QP5AwMBAQEBAQEAAAAACAAAAgAOABAAAKwA3AAATNTsBHQE7AT0BOwEdASsBHQE7ARkBKwE9ASsBHQErAR0BKwEZATsBPQErAQU1KwEdATsBPQE7AYBAQMDAQEBAQICAgIBAQEBAwMCAgEBAAYCAgEBAQEADwEBAQEBAQEBAQP7A/sCAgEBAQEABQAFAQEDAQICAQEAAAQAAAIADgAQAADsAABkBOwEdATsBPQE7AT0BOwE9ATsBHQErAR0BKwEdASsBHQE7AR0BOwEdATsBHQErAT0BKwE9ASsBHQErAYCAQEBAQEBAgIBAQEBAQEBAQEBAQEDAwEBAQECAgAJAAcDAwEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQICAAAEAAACAA4AEAAAjAAABNSkBGQErARkBKwEdASsBHQErAR0BKwE9ATsBPQE7AT0BOwEBgAEAAQCAgEBAQEBAQICAQEBAQEBAA8BA/kD+QAGAAYBAQICAwMDAwICAQEAAAAEAAACAA4AEAAAnAAAZATsBHQE7AR0BOwE9ATsBPQE7ARkBKwE9ASsBHQErAT0BKwEdASsBgIBAQEBAQECAgICAQEBAQEBAgIACQAHAQEBAQEBAQED+QP5AwMBAQEBAwMAAAAABAAAAgAOABAAAFwAAGQE7AR0BOwE9ATsBGQErAT0BKwEdASsBgIDAwICAgIDAwICAAkABwMDAwMD+QP5AwMDAwAAAAAIAAACAA4AEAAAXAB8AABM1KQEdATsBGQErAR0BKQE9ASsBGQE7AQERKwEZATsBgAFAAUBAQEBA/sD+wEBAQEACAMDAwMADwEBAQP7A/sBAQEBAAUABQP7AAUD+wP7AAAEAAACAA4AEAAAPAAAZASkBGQErARkBKwEZASsBAcABwICAwMCAgAJAAcD+QP5AAYABgP6A/oAAAgAAAIADgAQAABMAGwAAGQEpAR0BOwEdASsBHQEpAR0BKwEBNSsBHQE7AQGAAYBAQEBA/wD/AICAAoDAwMDAAkABwEBAwMBAQICAAkDAwMAAAAAAAQAAAIADgAQAADcAAAE1KQEdATsBHQErAT0BKwEdASsBHQE7AR0BOwE9ATsBHQErAR0BKQE9ASsBPQErAT0BOwE9ATsBAQABAAEAQECAgICAQEBAQICAgIBAQP8A/wBAQEBAQEBAQAPAQEBAQEBAQEBAwMBAQEBAQEBAQEBAQEDAwEBAAAAAAAEAgACAA4AEAAAPAAATNSkBHQErARkBKwEZASsBgAGAAYCAgICAgIADwEBAQP6A/oABgAGAAAAAAQAAAIADgAQAACcAABE1OwEdATsBPQE7ARkBKwEdASkBPQErAT0BOwEdATsBPQEpAT0BKwGAgMDAgIBAQP7A/sBAQICAwMD/AP8AQEADQMDAwMDA/oD+gEBAQEBAQEBAgIBAQAAAAAADAAAAgAOABAAAJwAvADcAAAE1OwEdATsBHQE7AR0BKwEdASsBHQErAT0BKwE9ASsBPQE7AT0BOwERNSsBHQE7ASU1KwEdATsBAYBAQICAQEBAQICAQECAgEBAQECAgEBAQEABAEBAQEADwEBAQEBAwMBAQEBAQEBAQMDAQED+wMDAwMDAwMAAAQAAAIADgAQAAEcAABE1OwEdATsBHQE7AT0BOwE9ATsBHQErAR0BKwEdATsBHQE7AR0BKwE9ASsBPQErAR0BKwEdASsBPQE7AT0BOwE9ASsBPQErAYCAQEBAQEBAgIBAQEBAQEBAQICAQEBAQEBAgIBAQEBAQEBAQAOAgEBAQEBAQEBAgIBAQEBAQECAgEBAQEBAQEBAgIBAQEBAQEAAAAEAAAAAA4AEAAAXAAAZATsBGQE7ARkBOwEZATsBHQErAT0BKQGAgICAgIBAQICA/sD+wAJAAcD+gP6AAYABgP6A/oCAgEBAAAEAAACAA4AEAAAXAAARNTsBHQE7AT0BOwEZASsBPQEpAT0BKwGAgMDAgICAgP8A/wBAQANAwMDAwMD+QP5AwMBAQAAAAQAAAIADgAQAABcAABkBOwEZATsBGQE7ARkBOwEZATsBGQEpAYCAQEBAQEBAgID+QP5AAkABwP6A/oABgAGA/oD+gAGAAYD+QP5AAAEAAAAABAAEAAAfAAAZATsBGQE7ARkBOwEZATsBGQE7ARkBOwEdASsBPQEpAYCAQEBAQEBAgIBAQICA/oD+gAJAAcD+gP6AAYABgP6A/oABgAGA/oD+gICAQEAAAgAAAIADgAQAABcAHwAAETU7AR0BOwEdATsBHQErAR0BKQEZASsBATUrAR0BOwHAwMDAQEBAQP7A/sBAQAKAgICAgAPAQICAQEDAwEBAAYABgP5AwMDAAAIAAACAA4AEAAAbACMAABkBOwEdATsBHQE7AT0BOwEZASsBPQErAR0BKQEBNSsBHQE7AYCAgIBAQICAgIBAQP8A/wABgEBAQEACQAHAgIBAQMDA/kD+QEBAQEABQMDAwAAAAAACAAAAgAOABAAAEwAbAAAZATsBHQEpAR0BOwEdASsBHQEpAQE1KwEdATsBgIABAAEAQEBAQP6A/oACgMDAwMACQAHAgIBAQMDAQEABQMDAwAAAAAABAAAAgAOABAAAPwAAEzUpAR0BOwEdATsBHQErAR0BKwEdASkBPQErAT0BOwEdATsBPQE7AT0BKQE9ASkBPQErAT0BKwEdASsBPQE7AYABAAEAQEBAQEBAQED/AP8AQECAgICAQED/AP8AAQABAEBAgICAgEBAA8BAQEBAQMDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAIAAACABAAEAAAnAC8AABkBOwEdATsBPQE7AT0BOwEdATsBGQErAR0BKwE9ASsBPQErAR0BKwEBESsBGQE7AYCAQEBAQMDAQEBAQMDAQEBAQICAAwBAQEBAAkABwMDAgIBAQEBA/sD+wEBAQECAgMDAAcABQP7A/sAAAAACAAAAgAOABAAAIwAvAAATNSkBGQErAT0BKwEdASsBHQErAT0BOwE9ATsBPQErAT0BOwEFNSsBHQE7AR0BOwGAAYABgICAQEBAQMDAQEBAQICAQEACAMDAQECAgAPAQP5A/kCAgEBAQEBAQEBAQEDAwMDAgIBAQAACAAAAgAOAAwAAGwAjAAATNSkBHQE7ARkBKQE9ASsBPQE7AT0BKQE9ASkBATUrAR0BOwGAAUABQEBA/oD+gEBAQEABAAEA/wD/AAIAwMDAwALAQEBA/wD/AEBAQEBAQEBA/sBAQEAAAAIAAACAA4AEAAAjAC8AABM1KQEdASkBHQE7AT0BOwEdATsBHQErAR0BKQE9ASsBGQE7AQE1KwEdASsBHQE7AYABgAGA/sD+wEBAwMBAQEBA/sD+wEBAQEACAICAQEDAwAPAQEBAgIBAQEBAwMBAQEBAAUABQP5AwEBAgIAAAAAAAwAAAIADgAMAABcAHwAnAAAZASkBHQE7AR0BKwEdATsBHQErAR0BKQEBNSsBHQE7ARU1KwEdATsBAYABgEBAQEBAQEBA/oD+gAKAwMDAwMDAwMABwAFAQEBAQEBAQEBAQAHAQEBAwEBAQAAAAQCAAIADgAMAAAsAABMRKQEdASkBGQErAYABgAGA/wD/AICAAcABQEBA/wD/AAAAAgAAAAADgAMAABsAIwAAEzUpARkBKwEdASkBPQEpAT0BKQE9ASsBPQE7AQU1KwEdATsBgAGAAYBAQP7A/sABAAEA/wD/AEBAQEACAMDAwMACwED+wP7AQEBAQEBAQECAgICAgIAAAAACAAAAgAOAAwAAGwAjAAATNSkBHQE7AR0BKQEdASkBHQEpAT0BKwE9ATsBBTUrAR0BOwGAAUABQEBA/sD+wAEAAQD+wP7AQEBAQAIAwMDAwALAQEBAgIBAQEBAQEDAwEBAQEAAAQAAAIADgAMAADcAABE1OwEdATsBPQE7AR0BOwE9ATsBHQErAR0BOwEdASsBPQErAR0BKwE9ASsBHQErAT0BOwE9ASsBgIBAQEBAQECAgEBAQECAgEBAQEBAQICAQEBAQAKAgICAgICAgICAgIBAQICAgICAgICAgICAgEBAAAABAAAAgAOAAwAAJwAAETUpAR0BOwEdASsBHQE7AR0BKwEdASkBPQEpAT0BKQE9ASkBPQEpAQGAAYBAQEBAQEBAQP6A/oABQAFA/wD/AAEAAQD+wP7AAsBAQEBAQEBAQEBAQEBAQEBAQEBAAAABAAAAgAOAAwAAJwAAGQE7AR0BOwE9ATsBPQE7AT0BOwEZASsBPQErAR0BKwEdASsBHQErAYCAQEBAQEBAgICAgEBAQEBAQICAAcABQMDAQEBAQEBA/sD+wMDAQEBAQEBAAAAAAgAAAIADgAQAAC8APwAAEzU7AR0BOwE9ATsBHQErAR0BOwEZASsBPQErAR0BKwEdASsBHQErARkBOwE9ASsBBTUrAR0BOwE9ATsBPQE7AYBAQMDAQEBAQICAgIBAQEBAQECAgICAQEACAMDAQEBAQEBAA8BAQEBAQEBAQED+wP7AwMBAQEBAQEABQAFAQEDAQMDAQEBAQAABAAAAgAOAAwAALwAAGQE7AR0BOwE9ATsBPQE7AR0BKwEdASsBHQE7AR0BOwEdASsBPQErAT0BKwEdASsBgICAgEBAgIBAQEBAQEBAQICAQECAgICAAcABQICAQEBAQEBAQEBAQEBAQEBAQEBAgIAAAQAAAIADgAMAABsAAAE1KQEZASsBGQErAR0BKwEdASsBPQE7AT0BOwEBAAFAAUCAgICAQECAgEBAQEACwED+wP7AAQABAICAgICAgICAAAABAAAAgAOAAwAAJwAAGQE7AR0BOwEdATsBPQE7AT0BOwEZASsBPQErAR0BKwE9ASsBHQErAYCAQEBAQEBAgICAgEBAQEBAQICAAcABQEBAQEBAQEBA/sD+wMDAgICAgMDAAAAAAQAAAIADgAMAABcAABkBOwEdATsBPQE7ARkBKwE9ASsBHQErAYCAwMCAgICAwMCAgAHAAUCAgICA/sD+wICAgIAAAAACAAAAgAOAAwAAFwAfAAATNSkBHQE7AR0BKwEdASkBPQErAT0BOwEFNSsBHQE7AYABQAFAQEBAQP7A/sBAQEBAAgDAwMDAAsBAQEDAwEBAQEDAwMDAwMAAAQAAAIADgAMAAA8AABkBKQEZASsBGQErARkBKwEBwAHAgIDAwICAAcABQP7A/sABAAEA/wD/AAACAAAAAAOAAwAAEwAbAAAZASkBHQE7AR0BKwEdASkBHQErAQE1KwEdATsBAYABgEBAQED/AP8AgIACgMDAwMABgAGAQECAgEBAgIACAICAgAAAAAABAAAAgAOAAwAAFwAAEzUpAR0BKQEdASkBHQEpAT0BKwE9ATsBgAGAAYD+wP7AAUABQP6A/oBAQEBAAsBAQEDAwEBAQEDAwAABAIAAgAOAAwAADwAAEzUpAR0BKwEZASsBGQErAYABgAGAgICAgICAAsBAQED/AP8AAQABAAAAAAEAAAAAA4ADAAAfAAARNTsBHQE7AT0BOwEZASsBHQEpAT0BKQE9ASkBPQErAYCAwMCAgEBA/sD+wAEAAQD/AP8AQEACQMDAwMDA/sD+wEBAQEBAQEBAAAADAAAAAAOAA4AAJwAvADcAAAE1OwEdATsBHQE7AR0BKwEdASsBHQErAT0BKwE9ASsBPQE7AT0BOwERNSsBHQE7ASU1KwEdATsBAYBAQICAQEBAQICAQECAgEBAQECAgEBAQEABAEBAQEADQEBAQEBAwMBAQEBAQEBAQMDAQED+wMDAwMDAwMAAAQAAAIADgAMAACcAABE1OwEdATsBPQE7AR0BKwEdATsBHQErAT0BKwEdASsBPQE7AT0BKwGAgMDAgICAgICAgIDAwICAgICAgAKAgEBAQECAgEBAgIBAQEBAgIBAQAAAAQAAAAADgAMAABcAABkBOwEZATsBGQE7ARkBOwEdASsBPQEpAYCAgICAgEBAgID+wP7AAcABQP8A/wABAAEA/wD/AICAQEAAAQAAAIADgAMAABcAABE1OwEdATsBPQE7ARkBKwE9ASkBPQErAYCAwMCAgICA/wD/AEBAAoCAgICAgP7A/sCAgEBAAAABAAAAgAOAAwAAFwAAGQE7ARkBOwEZATsBGQE7ARkBOwEZASkBgIBAQEBAQECAgP5A/kABwAFA/wD/AAEAAQD/AP8AAQABAP7A/sAAAQAAAAAEAAMAAB8AABkBOwEZATsBGQE7ARkBOwEZATsBGQE7AR0BKwE9ASkBgIBAQEBAQECAgEBAgID+gP6AAcABQP8A/wABAAEA/wD/AAEAAQD/AP8AgIBAQAACAAAAgAOAAwAAFwAfAAARNTsBHQE7AR0BOwEdASsBHQEpARkBKwEBNSsBHQE7AcDAwMBAQEBA/sD+wEBAAoCAgICAAsBAgIBAQEBAQEABAAEA/sBAQEAAAgAAAIADgAMAABsAIwAAGQE7AR0BOwEdATsBPQE7ARkBKwE9ASsBHQEpASU1KwEdATsBgICAgEBAgICAgEBA/wD/AAGAQEBAQAHAAUCAgEBAwMD+wP7AQEBAQMBAQEAAAgAAAIADgAMAABMAGwAAGQE7AR0BKQEdATsBHQErAR0BKQElNSsBHQE7AYCAAQABAEBAQED+gP6AAoDAwMDAAcABQICAQEBAQEBAwEBAQAABAAAAgAOAAwAALwAAEzUpAR0BOwEdASsBHQEpAT0BKwE9ATsBHQE7AT0BKwE9ATsBPQErAR0BKwE9ATsBgAFAAUBAQEBA/sD+wEBAgIDAwICAgIDAwICAQEACwEBAQMDAQEBAQEBAQEBAQEBAQEBAQEBAAAIAAACABAADAAAnAC8AABkBOwEdATsBPQE7AT0BOwEdATsBHQErAR0BKwE9ASsBPQErAR0BKwEBNSsBHQE7AYCAQEBAQMDAQEBAQMDAQEBAQICAAwBAQEBAAcABQICAQEBAQEBAwMBAQEBAQECAgAFAwMDAAAAAAAIAAACAA4ADAAAbACMAABM1KQEZASsBPQErAR0BKwE9ATsBPQErAT0BOwEFNSsBHQE7AYABgAGAgIDAwICAQEBAQEBAAgDAwMDAAsBA/sD+wICAgICAgEBAQEBAQEBAAAIAAACAA4AEAAArADMAAAE1OwEdATsBHQE7AR0BOwEdASkBHQEpAR0BKQE9ASsBPQE7AT0BOwE9ASsBATUrAR0BOwEBAEBAQECAgEBA/sD+wAEAAQD+wP7AQEBAQICAQEABgMDAwMADwEBAQEBAQECAgEBAQEBAQMDAQEBAQP7AQEBAAAQAAACAA4AEAAAbACMAKwAzAAATNSkBHQE7AR0BKQEdASkBHQEpAT0BKwE9ATsBBTUrAR0BOwEDNTsBHQErASU1OwEdASsBgAFAAUBAQP7A/sABAAEA/sD+wEBAQEACAMDAwMCAgICAgP6AgICAgALAQEBAgIBAQEBAQEDAwEBAQEABwEBAQEBAQEAAAQAAAAADgAQAAC8AABM1OwEdATsBHQErAR0BOwEdATsBHQErAR0BKwE9ATsBPQErAR0BKwEZASsBPQE7AYCAgICAgIDAwEBAQECAgEBAgICAgEBAQEADwEBAQEBAQEBAQMDAQEBAQMDAwMABQAFAQEAAAAABAIAAgAOABAAAGwAAATU7AR0BKwEdATsBHQEpARkBKwEZATsBPQE7AQIAQEBAQMDA/wD/AICAgIBAQAPAQEBAQEBAQP8A/wABQAFAQEAAAAEAAACAA4ADAAAvAAATNSkBHQE7AR0BKwE9ASsBHQE7AR0BKwEdATsBPQE7AR0BKwEdASkBPQErAT0BOwGAAUABQEBAgIDAwICAgIDAwICAQED+wP7AQEBAQALAQEBAQEBAQEBAQEBAQEBAQEBAQEBAwMAAAQAAAIADgAMAACcAABM1KQEdASkBHQEpAR0BOwEdASsBHQEpAT0BKQE9ASkBPQErAT0BOwGAAUABQP8A/wABAAEAQEBAQP6A/oABQAFA/wD/AEBAQEACwEBAQEBAQEBAQEBAQEBAQEBAQEAAAgCAAIADgAQAABMAGwAAATU7ARkBOwEdASkBPQE7AT0BKwETNTsBHQErAQEAwMCAgP6A/oCAgEBAgICAgIACwED/AP8AQEBAQMDAAUBAQEAAAAAAAwCAAIADgAQAABMAGwAjAAABNTsBGQE7AR0BKQE9ATsBPQErAQE1OwEdASsBJTU7AR0BKwEBAMDAgID+gP6AgIBAQAEAgICAgP6AgICAgALAQP8A/wBAQEBAwMABQEBAQEBAQEAAAAACAIAAAAMABAAAEwAbAAABNTsBGQErAR0BKQE9ATsBGQErARM1OwEdASsBAYDAwEBA/wD/AMDAQECAgICAgALAQP7A/sBAQEBAAQABAAFAQEBAAAACAAAAgAOAAwAAIwArAAABNTsBHQE7AR0BOwEdASsBHQErAT0BKwEdASsBPQE7AT0BOwEBNSsBHQE7AQEAgICAgEBAQEDAwEBAgIBAQEBAAYBAQEBAAsBAQEBAQICAQEDAwMDAwMBAQP8AgICAAAAAAgAAAIADgAMAACMAKwAAGQE7AR0BOwE9ATsBHQE7AR0BOwEdASsBHQErAT0BKwEdASsBATUrAR0BOwGAgEBAQECAgEBAQEDAwEBAgIACgEBAQEABwAFAgICAgEBAQECAgEBAgICAgAEAgICAAAAAAAEAAACAA4AEAAAnAAATNTsBHQE7AR0BKwEdATsBHQE7AR0BKwE9ASsBHQErARkBKwE9ATsBgICAgICAgMDAQECAgICAgIBAQEBAA8BAQEBAQEBAQEDAwMDAwMABQAFAQEAAAAACAAAAgAOABAAALwA/AAAZATsBHQE7AT0BOwE9ATsBHQErAR0BKwEdATsBHQE7AR0BKwE9ASsBPQErAR0BKwEBNTsBHQErAR0BKwE9ATsBgICAgEBAgIBAQEBAQEBAQICAQECAgICAAgBAQEBAQEBAQAHAAUCAgEBAQEBAQEBAQEBAQEBAQEBAQICAA0BAQEBAQEBAAAAAAAIAAACAA4AEAAAnADcAABkBOwEdATsBPQE7AT0BOwE9ATsBGQErAT0BKwEdASsBHQErAR0BKwEBNTsBHQE7AR0BKwE9ASsBgIBAQEBAQECAgICAQEBAQEBAgIABAEBAQEBAQEBAAcABQMDAQEBAQEBA/sD+wMDAQEBAQEBAA0BAQEBAQEBAAAACAAAAAAOABAAALwA3AAATNTsBHQE7AT0BOwEdASsBHQE7ARkBKwEdASkBPQEpAT0BKQE9ASsBPQE7AT0BKwEBNSsBHQE7AYBAQMDAQEBAQICAQED+wP7AAQABAP8A/wBAQICAQEACAMDAwMADwEBAQEBAQEBAQP7A/sBAQEBAQEBAQMDAQED+wMDAwAAAAAABAAAAAAOAAwAAFwAAGQE7ARkBOwEZATsBGQErAR0BKwE9ASsBgIDAwICAwMBAQMDAAcABQP8A/wABAAEA/sD+wEBAQEAAAAABAAACAAOAAoAABwAAETUpAR0BKQEBwAHA/kD+QAJAQEBAAAABAAACAAQAAoAABwAAETUpAR0BKQECAAIA/gD+AAJAQEBAAAABAAACAAOAAoAABwAAETUpAR0BKQEBwAHA/kD+QAJAQEBAAAABAQACgAKABAAADwAAATU7AR0BKwEdASsBPQE7AQGAgIBAQICAQEADwEBAQICAgIAAAAAAAQEAAoACgAQAAA8AAAE1OwEdASsBHQErAT0BOwEBgICAQECAgEBAA4CAgIBAQEBAAAAAAAEBAACAAoACAAAPAAABNTsBHQErAR0BKwE9ATsBAYCAgEBAgIBAQAGAgICAQEBAQAAAAAABAIACgAOABAAAHwAAATU7AR0BOwE9ATsBHQErAR0BKwE9ASsBHQErAT0BOwEBAICAQECAgEBAgIBAQICAQEADwEBAQEBAQECAgICAgICAgAAAAAABAIACgAOABAAAHwAAATU7AR0BOwE9ATsBHQErAR0BKwE9ASsBHQErAT0BOwEBAICAQECAgEBAgIBAQICAQEADgICAgICAgIBAQEBAQEBAQAAAAAABAAAAgAMAAgAAHwAAEzU7AR0BOwE9ATsBHQErAR0BKwE9ASsBHQErAT0BOwGAgIBAQICAQECAgEBAgIBAQAGAgICAgICAgEBAQEBAQEBAAAEAgACAA4AEAAAXAAABNTsBHQE7AR0BKwEZASsBGQErAT0BOwEBgICAgICAgICAgICAgAOAgICAQED/AP8AAQABAEBAAAAAAAEAgACAA4AEAAAnAAABNTsBHQE7AR0BKwEdATsBHQErAR0BKwE9ASsBPQE7AT0BKwE9ATsBAYCAgICAgICAgICAgICAgICAgICAgAOAgICAQEBAQEBAgICAgEBAQEBAQAAAAAABAQABAAMAAwAAFwAAATU7AR0BOwEdASsBHQErAT0BKwE9ATsBAYCAgEBAQECAgEBAQEACwEBAQICAQEBAQICAAAAAAAMAAACAA4ABgAAHAA8AFwAAATU7AR0BKwElNTsBHQErASU1OwEdASsBAwBAQEBA/oBAQEBA/oBAQEBAAQCAgICAgICAgICAgAAAAAAFAAAAgAQABAAAPwBPAFcAZwBvAAABNTsBHQErAR0BKwEdASsBHQEpAR0BKwEdASkBPQErAR0BKwEdASsBPQE7AT0BOwE9ATsBPQE7AT0BOwE9ATsBATUrAR0BOwEdATsBPQErAQU1KwEdATsBATU7AR0BKwEdASsBPQE7ARc1KwEdATsBAwBAQEBAQEBAQAEAAQBAQP8A/wBAQEBAQEBAQEBAQEBAQEBAQED/AEBAQEBAQEBAAYBAQEBA/QCAgEBAgIBAQIBAQEBAA8BAQEBAQEBAQECAgEBAgIBAQEBAQEBAQEBAQEBAQEBA/kBAQEBAQEBAQEBAQALAQICAQECAgEBAQEAAAAABAQABAAMAA4AAJwAAATU7AR0BKwEdASsBHQE7AR0BOwEdASsBPQErAT0BKwE9ATsBPQE7AQIAgIBAQEBAQEBAQICAQEBAQEBAQEADQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAQCAAQACgAOAACcAABM1OwEdATsBHQE7AR0BKwEdASsBHQErAT0BOwE9ATsBPQErAT0BKwGAgIBAQEBAQEBAQICAQEBAQEBAQEADQEBAQEBAQEBAQEBAQEBAQEBAQEAAAQAAAIADgAQAADcAAAE1OwEdASsBHQErAR0BKwEdASsBHQErAR0BKwEdASsBPQE7AT0BOwE9ATsBPQE7AT0BOwE9ATsBAwBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAA8BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAwAAAIADgAQAADcAPwBHAAABNTsBHQE7AR0BKwEdASsBHQE7AR0BOwEdASsBHQErAT0BKwE9ASsBPQE7AT0BKwE9ATsBPQE7AQU1KwEdATsBETUrAR0BOwEBgMDAQECAgICAgICAgEBAwMBAQICAQEBAQICAQEABAEBAQEBAQEBAA8BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA/kBAQEAAAwAAAAADgAQAACsAMwA7AAATNTsBHQE7AR0BOwE9ATsBHQE7AR0BKwEdASsBHQErAT0BKwEdASsBGQE7AQERKwEZATsBJTUrAR0BOwGAgIBAQEBAQEBAQEBAQEBAQEBAwMBAQAEAgICAgAGAQEBAQAPAQEBAgIBAQEBAwMBAQEBAgIBAQAGAAYD+wAFA/sD+wMDAwMAAAAAEAAAAgAOABAAABwAfACcAOwAAJTU7AR0BKwETNTsBHQE7AR0BKwEdASsBPQErAT0BOwEXNSsBHQE7ASUROwEdATsBGQErARkBKwEZASsBAgDAwMDAgEBAQEBAQEBAQEBAQIBAQEBA/QCAgEBAQEBAQEBAwEBAQANAQEBAwMBAQEBAwMDAwMDAQAHAQED+gP6AAYABgP6A/oAAAAABAIACAAOABAAAGwAAEzUpARkBKwE9ASsBHQErAT0BKwEdASsBPQErAYABgAGAQEBAQEBAQEBAQEBAA8BA/wD/AICAgIDAwMDAwMAAAQAAAIAEAAQAACcAAAE1OwEdASkBHQEpAR0BKwE9ASsBPQErAT0BKwE9ATsBPQE7AT0BOwEBgEBAAQABAP8A/wBAQEBAQEBAQEBAQEBAQAPAQICAwMCAgEBAQEBAQEBAQEBAQAAAAAABAAAAAAOABAAAJwAAATU7AR0BOwEdATsBHQE7AR0BKwEZASsBGQErAT0BOwE9ATsBPQE7AQGAQEBAQEBAQECAgMDAgIBAQEBAQEADwEBAQEBAQEBAQP8A/wABAAEAQEBAQEBAAAAAAAEAAACABAAEAAAnAAABNTsBHQE7AR0BOwEdATsBHQErAR0BKwEdASsBHQErAT0BKQE9ASkBAgBAQEBAQEBAQEBAQEBAQEBA/wD/AAEAAQADgIBAQEBAQEBAQEBAQEBAQICAwMAAAAAAAQAAAAADgAQAACcAAAEROwEZATsBHQErAR0BKwEdASsBHQErAT0BKwE9ASsBPQErAT0BOwEBAMDAgIBAQEBAQEBAQEBAQEBAQICAAwABAP8A/wBAQEBAQEBAQEBAQEBAQEBAAAIAAACAA4AEAAAvADcAABM1KQEdATsBHQE7ARkBKwEdASkBPQErAT0BOwE9ASkBPQErAT0BKwEdASsBPQE7AQE1KwEdATsBgAEAAQBAQEBAQED+wP7AQEBAQAEAAQBAQICAgIBAQAIAwMDAwAPAQEBAQED/AP8AQEBAQICAQEBAQEBAQEBAQP4AgICAAAAAAAIAAACAA4AEAAAXACcAAAE1OwEdATsBHQE7ARkBKQEZATsBPQE7AQU1KwEdASsBGQE7ARkBKwEBAMDAQEBAQP5A/kBAQEBAAQBAQEBAwMBAQAPAQEBAQED+wP7AAUABQEBAQEBAQP8A/wABAAEAAAABAAAAgAOABAAADwAAGQEpARkBKwEZASsBGQErAQHAAcCAgMDAgIACQAHA/kD+QAGAAYD+gP6AAAEAAACAA4AEAAA/AAARNSkBHQErAT0BKwEdATsBHQE7AR0BKwEdASsBHQE7AT0BOwEdASkBPQE7AT0BOwE9ATsBPQErAT0BKwE9ASsBAcABwICAgIBAQICAgIBAQICAgID+QP5AQEBAQEBAQEBAQEBAA8BAgIBAQEBAQEBAQEBAQEBAQICAQEBAQEBAQEBAQEBAAAABAAAAgAOABAAAGwAAAREpAR0BKwEZASsBPQErAT0BKwE9ATsBHQE7AQGAAQABAICAwMBAQEBAgIBAQALAAUBAQP6A/oBAQEBAQEBAQAAAAAMAAAEAA4ADAAAnAC8ANwAAEzU7AR0BOwE9ATsBHQE7AR0BKwEdASsBPQErAR0BKwE9ASsBPQE7AQU1KwEdATsBJTUrAR0BOwGAgIBAQICAQEBAQICAQECAgEBAQEABAICAgIABgICAgIACwEBAQEBAQECAgEBAQEBAQEBAgICAgICAgICAgAABAIAAAAOABAAAJwAAATU7AR0BOwEdASsBPQErARkBKwEdASsBPQErAT0BOwEdATsBGQE7AQIAgIBAQEBAQEBAQICAQEBAQEBAQEADwEBAQEBAQED+gP6AQEBAQEBAQEABgAGAAAAAAAIAAAEAAwADgAAfAD8AABM1OwEdATsBPQE7AR0BKwEdASsBPQErAR0BKwE9ATsBETU7AR0BOwE9ATsBHQErAR0BKwE9ASsBHQErAT0BOwGAgICAgEBAQECAgICAQEBAQICAgIBAQEBAgICAgEBAQEABwEBAQEBAQEBAQEBAQEBAQAHAQEBAQEBAQEBAQEBAQEBAAAABAAAAgAOABAAANwAAATU7AR0BKwEdATsBHQErAR0BOwEdASkBHQErAR0BKwE9ATsBPQErAT0BOwE9ASsBPQEpAT0BOwEDAEBAQEBAQMDAwMD+wP7AQEBAQEBAQEDAwMDAAUABQEBAA8BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAgEAAIADAAQAAAcALwAAJTUpAR0BKQEBNTsBHQErAR0BKwEdATsBHQE7AR0BKwE9ASsBPQErAT0BOwE9ATsBAQABAAEA/wD/AAEAgIBAQEBAQEBAQICAQEBAQEBAQEDAQEBAA0BAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAIBAACAAwAEAAAHAC8AACU1KQEdASkBETU7AR0BOwEdATsBHQErAR0BKwEdASsBPQE7AT0BOwE9ASsBPQErAQEAAQABAP8A/wCAgEBAQEBAQEBAgIBAQEBAQEBAQMBAQEADQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAEAAACAA4AEAAAfAAABNTsBHQE7AR0BOwEdATsBHQEpAT0BOwE9ATsBPQE7AQGAQEBAQEBAQED+QP5AQEBAQEBAA4CAgICAgICAQEBAQICAgIAAAAEAAACAA4AEAAAfAAAZATsBHQE7AR0BOwEdATsBHQErAR0BKwEdASsBHQErAUBAgICAgICAgICAgICAQEACQAHAQEBAQEBAQEBAQEBAQEAAAQAAAIADgAQAAB8AABE1KQEdASsBHQErAR0BKwEdASsBPQErAT0BKwE9ASsBAcABwEBAQEBAQEBAQEBAQEBAA8BAQECAgICAgICAgICAgIAAAAAAAQAAAIADgAQAAB8AAAE1OwEZASsBPQErAT0BKwE9ASsBPQE7AT0BOwE9ATsBAwBAQEBAgICAgICAgICAgICAA8BA/kD+QEBAQEBAQEBAQEBAQAAAAgAAAIADgAQAADcAXwAAATU7AR0BOwEdATsBHQE7AR0BKwEdASsBHQErAR0BKwE9ASsBPQErAT0BKwE9ATsBPQE7AT0BOwEXNSsBHQErAR0BKwEdATsBHQE7AR0BOwE9ATsBPQE7AT0BKwE9ASsBAYBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAgEBAQEBAQEBAQEBAQEBAQEBAQEBAA8BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAABAAAAgAOABAAANwAAATU7AR0BOwEdASsBHQErAR0BOwEdASsBPQErAT0BKwEdASsBHQErAT0BOwE9ASsBPQErAT0BOwEBgEBAwMBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAwMADgICAgEBAQEBAQICAQEBAQEBAQECAgEBAQEBAQAAAAAACAAAAgAOABAAALwBHAAABNTsBHQE7AR0BKwEdASsBHQE7AR0BKwE9ASsBHQErAT0BOwE9ASsBPQErAT0BOwEXNSsBHQErAR0BOwEdATsBPQE7AT0BKwEBgEBAwMBAQEBAQEBAQMDAQEBAQEBAQEDAwIBAQEBAQEBAQEBAQEADgICAgEBAQEBAQICAQEBAQICAQEBAQEBAQEBAQEBAQEBAQEBAAAEAAACAA4AEAAAvAAABNTsBHQE7AR0BOwEdATsBHQErAR0BOwEdASkBPQE7AT0BKwE9ATsBPQE7AT0BOwEBgEBAQEBAQEBAgIBAQP7A/sBAQICAQEBAQEBAA8BAQEBAQEBAgIBAQEBAQEBAQICAQEBAQAAAAgCAAIADgAQAAB8AJwAAATU7AR0BOwEdASsBHQE7AR0BKQE9ATsBPQErAT0BOwEFNSsBHQE7AQGAgICAgICAQED/AP8AQECAgICAAQCAgICAA4CAgICAgICAQEBAQICAgICAgICAAAABAAAAgAOABAAALwAAEzU7AR0BOwE9ATsBHQE7AR0BKwEdASsBHQErAR0BKwE9ASsBPQErAT0BKwE9ATsBgICAQECAgEBAQEBAQEBAQEBAQEBAQEBAQAPAQEBAQEBAQMDAQEBAQEBAQEBAQEBAwMAAAQAAAIADgAQAADcAAAE1OwEdATsBHQE7AR0BOwEdASsBHQErAR0BKwEdASsBPQErAT0BKwE9ASsBPQE7AT0BOwE9ATsBAYBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAA8BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAQAAAIADgAQAADcAAAEROwEdATsBHQE7AR0BOwEdASsBHQErAT0BOwE9ASsBPQErARkBKwEdASsBPQErAT0BOwE9ATsBAYBAQEBAQEBAQEBAQEBAQEBAQEBAQICAQEBAQICAAwABAEBAQEBAQEBAQEBAQEBAQED/AP8AQEBAQEBAQEAAAwCAAIADgAQAAAcAFwAfAAATESkBGQEpAQE1KQEdATsBHQErAR0BOwEFNSsBHQE7AYABgAGA/oD+gAKA/wD/AMDAgIDAwP8AQEBAQAJAAcD+QP5AAkDAQEBAQEBAwEBAQAAAAAEAgACAAwABgAALAAABNTsBHQEpAT0BOwEBgMDA/sD+wICAAUBAgIBAQAAAAwAAAIAEAAQAAC8ANwBHAAABNTsBHQE7AR0BOwEdASsBHQErAR0BKwE9ASsBHQErAT0BKwE9ASsBPQE7AT0BOwEXNSsBHQE7AQU1KwEdASsBHQE7AT0BOwECAEBAgIBAQEBAQECAgEBAQEBAQEBAQEDAwIBAQEBAAQBAQEBAQEBAQAPAQEBAQEDAwEBAQEBAQEBAQEBAQMDAQEBAQEBAgICAgEBAQEAAAQAAAIADgAQAACMAAAE1OwEdASsBHQEpARkBKwEZASsBGQErARkBKwE9ATsBPQE7AQEAwMCAgAEAAQCAgICAgIBAQEBAQEADwEBAQEBA/sD+wAEAAQD/AP8AAQABAEBAQEAAAAIAAACAA4AEAAAbACMAAAE1KQEZASsBGQErARkBKwEZASsBPQE7AT0BOwEFNSsBHQE7AQEAAUABQICAgICAgEBAQEBAQAGAgICAgAPAQP5A/kABAAEA/wD/AAEAAQBAQEBAQEBAQAAAAAASAN4AAQAAAAAAAABOAJ4AAQAAAAAAAQAOAQsAAQAAAAAAAgAHASoAAQAAAAAAAwAqAYgAAQAAAAAABAAOAdEAAQAAAAAABQANAfwAAQAAAAAABgAMAiQAAQAAAAAADRE1JJ0AAQAAAAAADgAaNgkAAwABBAkAAACcAAAAAwABBAkAAQAcAO0AAwABBAkAAgAOARoAAwABBAkAAwBUATIAAwABBAkABAAcAbMAAwABBAkABQAaAeAAAwABBAkABgAYAgoAAwABBAkADSJqAjEAAwABBAkADgA0NdMAKABjACkAIAAyADAAMQAxACAAQwBvAGQAeQAgACIAQwBvAGQAZQBNAGEAbgAzADgAIgAgAEIAbwBpAHMAYwBsAGEAaQByAC4AIABSAGUAbABlAGEAcwBlAGQAIAB1AG4AZABlAHIAIAB0AGgAZQAgAFMASQBMACAATwBwAGUAbgAgAEYAbwBuAHQAIABMAGkAYwBlAG4AcwBlAC4AAChjKSAyMDExIENvZHkgIkNvZGVNYW4zOCIgQm9pc2NsYWlyLiBSZWxlYXNlZCB1bmRlciB0aGUgU0lMIE9wZW4gRm9udCBMaWNlbnNlLgAAUAByAGUAcwBzACAAUwB0AGEAcgB0ACAAMgBQAABQcmVzcyBTdGFydCAyUAAAUgBlAGcAdQBsAGEAcgAAUmVndWxhcgAARgBvAG4AdABGAG8AcgBnAGUAIAAyAC4AMAAgADoAIABQAHIAZQBzAHMAIABTAHQAYQByAHQAIAAyAFAAIAA6ACAAMQAyAC0ANgAtADIAMAAxADEAAEZvbnRGb3JnZSAyLjAgOiBQcmVzcyBTdGFydCAyUCA6IDEyLTYtMjAxMQAAUAByAGUAcwBzACAAUwB0AGEAcgB0ACAAMgBQAABQcmVzcyBTdGFydCAyUAAAVgBlAHIAcwBpAG8AbgAgADIALgAxADQAIAAAVmVyc2lvbiAyLjE0IAAAUAByAGUAcwBzAFMAdABhAHIAdAAyAFAAAFByZXNzU3RhcnQyUAAAQwBvAHAAeQByAGkAZwBoAHQAIAAoAGMAKQAgADIAMAAxADEALAAgAEMAbwBkAHkAIAAiAEMAbwBkAGUATQBhAG4AMwA4ACIAIABCAG8AaQBzAGMAbABhAGkAcgAgACgAYwBvAGQAeQBAAHoAbwBuAGUAMwA4AC4AbgBlAHQAKQAsAAoAdwBpAHQAaAAgAFIAZQBzAGUAcgB2AGUAZAAgAEYAbwBuAHQAIABOAGEAbQBlACAAUAByAGUAcwBzACAAUwB0AGEAcgB0AC4ACgAKAFQAaABpAHMAIABGAG8AbgB0ACAAUwBvAGYAdAB3AGEAcgBlACAAaQBzACAAbABpAGMAZQBuAHMAZQBkACAAdQBuAGQAZQByACAAdABoAGUAIABTAEkATAAgAE8AcABlAG4AIABGAG8AbgB0ACAATABpAGMAZQBuAHMAZQAsACAAVgBlAHIAcwBpAG8AbgAgADEALgAxAC4ACgBUAGgAaQBzACAAbABpAGMAZQBuAHMAZQAgAGkAcwAgAGMAbwBwAGkAZQBkACAAYgBlAGwAbwB3ACwAIABhAG4AZAAgAGkAcwAgAGEAbABzAG8AIABhAHYAYQBpAGwAYQBiAGwAZQAgAHcAaQB0AGgAIABhACAARgBBAFEAIABhAHQAOgAKAGgAdAB0AHAAOgAvAC8AcwBjAHIAaQBwAHQAcwAuAHMAaQBsAC4AbwByAGcALwBPAEYATAAKAAoACgAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ACgBTAEkATAAgAE8AUABFAE4AIABGAE8ATgBUACAATABJAEMARQBOAFMARQAgAFYAZQByAHMAaQBvAG4AIAAxAC4AMQAgAC0AIAAyADYAIABGAGUAYgByAHUAYQByAHkAIAAyADAAMAA3AAoALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAC0ALQAtAAoACgBQAFIARQBBAE0AQgBMAEUACgBUAGgAZQAgAGcAbwBhAGwAcwAgAG8AZgAgAHQAaABlACAATwBwAGUAbgAgAEYAbwBuAHQAIABMAGkAYwBlAG4AcwBlACAAKABPAEYATAApACAAYQByAGUAIAB0AG8AIABzAHQAaQBtAHUAbABhAHQAZQAgAHcAbwByAGwAZAB3AGkAZABlAAoAZABlAHYAZQBsAG8AcABtAGUAbgB0ACAAbwBmACAAYwBvAGwAbABhAGIAbwByAGEAdABpAHYAZQAgAGYAbwBuAHQAIABwAHIAbwBqAGUAYwB0AHMALAAgAHQAbwAgAHMAdQBwAHAAbwByAHQAIAB0AGgAZQAgAGYAbwBuAHQAIABjAHIAZQBhAHQAaQBvAG4ACgBlAGYAZgBvAHIAdABzACAAbwBmACAAYQBjAGEAZABlAG0AaQBjACAAYQBuAGQAIABsAGkAbgBnAHUAaQBzAHQAaQBjACAAYwBvAG0AbQB1AG4AaQB0AGkAZQBzACwAIABhAG4AZAAgAHQAbwAgAHAAcgBvAHYAaQBkAGUAIABhACAAZgByAGUAZQAgAGEAbgBkAAoAbwBwAGUAbgAgAGYAcgBhAG0AZQB3AG8AcgBrACAAaQBuACAAdwBoAGkAYwBoACAAZgBvAG4AdABzACAAbQBhAHkAIABiAGUAIABzAGgAYQByAGUAZAAgAGEAbgBkACAAaQBtAHAAcgBvAHYAZQBkACAAaQBuACAAcABhAHIAdABuAGUAcgBzAGgAaQBwAAoAdwBpAHQAaAAgAG8AdABoAGUAcgBzAC4ACgAKAFQAaABlACAATwBGAEwAIABhAGwAbABvAHcAcwAgAHQAaABlACAAbABpAGMAZQBuAHMAZQBkACAAZgBvAG4AdABzACAAdABvACAAYgBlACAAdQBzAGUAZAAsACAAcwB0AHUAZABpAGUAZAAsACAAbQBvAGQAaQBmAGkAZQBkACAAYQBuAGQACgByAGUAZABpAHMAdAByAGkAYgB1AHQAZQBkACAAZgByAGUAZQBsAHkAIABhAHMAIABsAG8AbgBnACAAYQBzACAAdABoAGUAeQAgAGEAcgBlACAAbgBvAHQAIABzAG8AbABkACAAYgB5ACAAdABoAGUAbQBzAGUAbAB2AGUAcwAuACAAVABoAGUACgBmAG8AbgB0AHMALAAgAGkAbgBjAGwAdQBkAGkAbgBnACAAYQBuAHkAIABkAGUAcgBpAHYAYQB0AGkAdgBlACAAdwBvAHIAawBzACwAIABjAGEAbgAgAGIAZQAgAGIAdQBuAGQAbABlAGQALAAgAGUAbQBiAGUAZABkAGUAZAAsACAACgByAGUAZABpAHMAdAByAGkAYgB1AHQAZQBkACAAYQBuAGQALwBvAHIAIABzAG8AbABkACAAdwBpAHQAaAAgAGEAbgB5ACAAcwBvAGYAdAB3AGEAcgBlACAAcAByAG8AdgBpAGQAZQBkACAAdABoAGEAdAAgAGEAbgB5ACAAcgBlAHMAZQByAHYAZQBkAAoAbgBhAG0AZQBzACAAYQByAGUAIABuAG8AdAAgAHUAcwBlAGQAIABiAHkAIABkAGUAcgBpAHYAYQB0AGkAdgBlACAAdwBvAHIAawBzAC4AIABUAGgAZQAgAGYAbwBuAHQAcwAgAGEAbgBkACAAZABlAHIAaQB2AGEAdABpAHYAZQBzACwACgBoAG8AdwBlAHYAZQByACwAIABjAGEAbgBuAG8AdAAgAGIAZQAgAHIAZQBsAGUAYQBzAGUAZAAgAHUAbgBkAGUAcgAgAGEAbgB5ACAAbwB0AGgAZQByACAAdAB5AHAAZQAgAG8AZgAgAGwAaQBjAGUAbgBzAGUALgAgAFQAaABlAAoAcgBlAHEAdQBpAHIAZQBtAGUAbgB0ACAAZgBvAHIAIABmAG8AbgB0AHMAIAB0AG8AIAByAGUAbQBhAGkAbgAgAHUAbgBkAGUAcgAgAHQAaABpAHMAIABsAGkAYwBlAG4AcwBlACAAZABvAGUAcwAgAG4AbwB0ACAAYQBwAHAAbAB5AAoAdABvACAAYQBuAHkAIABkAG8AYwB1AG0AZQBuAHQAIABjAHIAZQBhAHQAZQBkACAAdQBzAGkAbgBnACAAdABoAGUAIABmAG8AbgB0AHMAIABvAHIAIAB0AGgAZQBpAHIAIABkAGUAcgBpAHYAYQB0AGkAdgBlAHMALgAKAAoARABFAEYASQBOAEkAVABJAE8ATgBTAAoAIgBGAG8AbgB0ACAAUwBvAGYAdAB3AGEAcgBlACIAIAByAGUAZgBlAHIAcwAgAHQAbwAgAHQAaABlACAAcwBlAHQAIABvAGYAIABmAGkAbABlAHMAIAByAGUAbABlAGEAcwBlAGQAIABiAHkAIAB0AGgAZQAgAEMAbwBwAHkAcgBpAGcAaAB0AAoASABvAGwAZABlAHIAKABzACkAIAB1AG4AZABlAHIAIAB0AGgAaQBzACAAbABpAGMAZQBuAHMAZQAgAGEAbgBkACAAYwBsAGUAYQByAGwAeQAgAG0AYQByAGsAZQBkACAAYQBzACAAcwB1AGMAaAAuACAAVABoAGkAcwAgAG0AYQB5AAoAaQBuAGMAbAB1AGQAZQAgAHMAbwB1AHIAYwBlACAAZgBpAGwAZQBzACwAIABiAHUAaQBsAGQAIABzAGMAcgBpAHAAdABzACAAYQBuAGQAIABkAG8AYwB1AG0AZQBuAHQAYQB0AGkAbwBuAC4ACgAKACIAUgBlAHMAZQByAHYAZQBkACAARgBvAG4AdAAgAE4AYQBtAGUAIgAgAHIAZQBmAGUAcgBzACAAdABvACAAYQBuAHkAIABuAGEAbQBlAHMAIABzAHAAZQBjAGkAZgBpAGUAZAAgAGEAcwAgAHMAdQBjAGgAIABhAGYAdABlAHIAIAB0AGgAZQAKAGMAbwBwAHkAcgBpAGcAaAB0ACAAcwB0AGEAdABlAG0AZQBuAHQAKABzACkALgAKAAoAIgBPAHIAaQBnAGkAbgBhAGwAIABWAGUAcgBzAGkAbwBuACIAIAByAGUAZgBlAHIAcwAgAHQAbwAgAHQAaABlACAAYwBvAGwAbABlAGMAdABpAG8AbgAgAG8AZgAgAEYAbwBuAHQAIABTAG8AZgB0AHcAYQByAGUAIABjAG8AbQBwAG8AbgBlAG4AdABzACAAYQBzAAoAZABpAHMAdAByAGkAYgB1AHQAZQBkACAAYgB5ACAAdABoAGUAIABDAG8AcAB5AHIAaQBnAGgAdAAgAEgAbwBsAGQAZQByACgAcwApAC4ACgAKACIATQBvAGQAaQBmAGkAZQBkACAAVgBlAHIAcwBpAG8AbgAiACAAcgBlAGYAZQByAHMAIAB0AG8AIABhAG4AeQAgAGQAZQByAGkAdgBhAHQAaQB2AGUAIABtAGEAZABlACAAYgB5ACAAYQBkAGQAaQBuAGcAIAB0AG8ALAAgAGQAZQBsAGUAdABpAG4AZwAsAAoAbwByACAAcwB1AGIAcwB0AGkAdAB1AHQAaQBuAGcAIAAtAC0AIABpAG4AIABwAGEAcgB0ACAAbwByACAAaQBuACAAdwBoAG8AbABlACAALQAtACAAYQBuAHkAIABvAGYAIAB0AGgAZQAgAGMAbwBtAHAAbwBuAGUAbgB0AHMAIABvAGYAIAB0AGgAZQAKAE8AcgBpAGcAaQBuAGEAbAAgAFYAZQByAHMAaQBvAG4ALAAgAGIAeQAgAGMAaABhAG4AZwBpAG4AZwAgAGYAbwByAG0AYQB0AHMAIABvAHIAIABiAHkAIABwAG8AcgB0AGkAbgBnACAAdABoAGUAIABGAG8AbgB0ACAAUwBvAGYAdAB3AGEAcgBlACAAdABvACAAYQAKAG4AZQB3ACAAZQBuAHYAaQByAG8AbgBtAGUAbgB0AC4ACgAKACIAQQB1AHQAaABvAHIAIgAgAHIAZQBmAGUAcgBzACAAdABvACAAYQBuAHkAIABkAGUAcwBpAGcAbgBlAHIALAAgAGUAbgBnAGkAbgBlAGUAcgAsACAAcAByAG8AZwByAGEAbQBtAGUAcgAsACAAdABlAGMAaABuAGkAYwBhAGwACgB3AHIAaQB0AGUAcgAgAG8AcgAgAG8AdABoAGUAcgAgAHAAZQByAHMAbwBuACAAdwBoAG8AIABjAG8AbgB0AHIAaQBiAHUAdABlAGQAIAB0AG8AIAB0AGgAZQAgAEYAbwBuAHQAIABTAG8AZgB0AHcAYQByAGUALgAKAAoAUABFAFIATQBJAFMAUwBJAE8ATgAgACYAIABDAE8ATgBEAEkAVABJAE8ATgBTAAoAUABlAHIAbQBpAHMAcwBpAG8AbgAgAGkAcwAgAGgAZQByAGUAYgB5ACAAZwByAGEAbgB0AGUAZAAsACAAZgByAGUAZQAgAG8AZgAgAGMAaABhAHIAZwBlACwAIAB0AG8AIABhAG4AeQAgAHAAZQByAHMAbwBuACAAbwBiAHQAYQBpAG4AaQBuAGcACgBhACAAYwBvAHAAeQAgAG8AZgAgAHQAaABlACAARgBvAG4AdAAgAFMAbwBmAHQAdwBhAHIAZQAsACAAdABvACAAdQBzAGUALAAgAHMAdAB1AGQAeQAsACAAYwBvAHAAeQAsACAAbQBlAHIAZwBlACwAIABlAG0AYgBlAGQALAAgAG0AbwBkAGkAZgB5ACwACgByAGUAZABpAHMAdAByAGkAYgB1AHQAZQAsACAAYQBuAGQAIABzAGUAbABsACAAbQBvAGQAaQBmAGkAZQBkACAAYQBuAGQAIAB1AG4AbQBvAGQAaQBmAGkAZQBkACAAYwBvAHAAaQBlAHMAIABvAGYAIAB0AGgAZQAgAEYAbwBuAHQACgBTAG8AZgB0AHcAYQByAGUALAAgAHMAdQBiAGoAZQBjAHQAIAB0AG8AIAB0AGgAZQAgAGYAbwBsAGwAbwB3AGkAbgBnACAAYwBvAG4AZABpAHQAaQBvAG4AcwA6AAoACgAxACkAIABOAGUAaQB0AGgAZQByACAAdABoAGUAIABGAG8AbgB0ACAAUwBvAGYAdAB3AGEAcgBlACAAbgBvAHIAIABhAG4AeQAgAG8AZgAgAGkAdABzACAAaQBuAGQAaQB2AGkAZAB1AGEAbAAgAGMAbwBtAHAAbwBuAGUAbgB0AHMALAAKAGkAbgAgAE8AcgBpAGcAaQBuAGEAbAAgAG8AcgAgAE0AbwBkAGkAZgBpAGUAZAAgAFYAZQByAHMAaQBvAG4AcwAsACAAbQBhAHkAIABiAGUAIABzAG8AbABkACAAYgB5ACAAaQB0AHMAZQBsAGYALgAKAAoAMgApACAATwByAGkAZwBpAG4AYQBsACAAbwByACAATQBvAGQAaQBmAGkAZQBkACAAVgBlAHIAcwBpAG8AbgBzACAAbwBmACAAdABoAGUAIABGAG8AbgB0ACAAUwBvAGYAdAB3AGEAcgBlACAAbQBhAHkAIABiAGUAIABiAHUAbgBkAGwAZQBkACwACgByAGUAZABpAHMAdAByAGkAYgB1AHQAZQBkACAAYQBuAGQALwBvAHIAIABzAG8AbABkACAAdwBpAHQAaAAgAGEAbgB5ACAAcwBvAGYAdAB3AGEAcgBlACwAIABwAHIAbwB2AGkAZABlAGQAIAB0AGgAYQB0ACAAZQBhAGMAaAAgAGMAbwBwAHkACgBjAG8AbgB0AGEAaQBuAHMAIAB0AGgAZQAgAGEAYgBvAHYAZQAgAGMAbwBwAHkAcgBpAGcAaAB0ACAAbgBvAHQAaQBjAGUAIABhAG4AZAAgAHQAaABpAHMAIABsAGkAYwBlAG4AcwBlAC4AIABUAGgAZQBzAGUAIABjAGEAbgAgAGIAZQAKAGkAbgBjAGwAdQBkAGUAZAAgAGUAaQB0AGgAZQByACAAYQBzACAAcwB0AGEAbgBkAC0AYQBsAG8AbgBlACAAdABlAHgAdAAgAGYAaQBsAGUAcwAsACAAaAB1AG0AYQBuAC0AcgBlAGEAZABhAGIAbABlACAAaABlAGEAZABlAHIAcwAgAG8AcgAKAGkAbgAgAHQAaABlACAAYQBwAHAAcgBvAHAAcgBpAGEAdABlACAAbQBhAGMAaABpAG4AZQAtAHIAZQBhAGQAYQBiAGwAZQAgAG0AZQB0AGEAZABhAHQAYQAgAGYAaQBlAGwAZABzACAAdwBpAHQAaABpAG4AIAB0AGUAeAB0ACAAbwByAAoAYgBpAG4AYQByAHkAIABmAGkAbABlAHMAIABhAHMAIABsAG8AbgBnACAAYQBzACAAdABoAG8AcwBlACAAZgBpAGUAbABkAHMAIABjAGEAbgAgAGIAZQAgAGUAYQBzAGkAbAB5ACAAdgBpAGUAdwBlAGQAIABiAHkAIAB0AGgAZQAgAHUAcwBlAHIALgAKAAoAMwApACAATgBvACAATQBvAGQAaQBmAGkAZQBkACAAVgBlAHIAcwBpAG8AbgAgAG8AZgAgAHQAaABlACAARgBvAG4AdAAgAFMAbwBmAHQAdwBhAHIAZQAgAG0AYQB5ACAAdQBzAGUAIAB0AGgAZQAgAFIAZQBzAGUAcgB2AGUAZAAgAEYAbwBuAHQACgBOAGEAbQBlACgAcwApACAAdQBuAGwAZQBzAHMAIABlAHgAcABsAGkAYwBpAHQAIAB3AHIAaQB0AHQAZQBuACAAcABlAHIAbQBpAHMAcwBpAG8AbgAgAGkAcwAgAGcAcgBhAG4AdABlAGQAIABiAHkAIAB0AGgAZQAgAGMAbwByAHIAZQBzAHAAbwBuAGQAaQBuAGcACgBDAG8AcAB5AHIAaQBnAGgAdAAgAEgAbwBsAGQAZQByAC4AIABUAGgAaQBzACAAcgBlAHMAdAByAGkAYwB0AGkAbwBuACAAbwBuAGwAeQAgAGEAcABwAGwAaQBlAHMAIAB0AG8AIAB0AGgAZQAgAHAAcgBpAG0AYQByAHkAIABmAG8AbgB0ACAAbgBhAG0AZQAgAGEAcwAKAHAAcgBlAHMAZQBuAHQAZQBkACAAdABvACAAdABoAGUAIAB1AHMAZQByAHMALgAKAAoANAApACAAVABoAGUAIABuAGEAbQBlACgAcwApACAAbwBmACAAdABoAGUAIABDAG8AcAB5AHIAaQBnAGgAdAAgAEgAbwBsAGQAZQByACgAcwApACAAbwByACAAdABoAGUAIABBAHUAdABoAG8AcgAoAHMAKQAgAG8AZgAgAHQAaABlACAARgBvAG4AdAAKAFMAbwBmAHQAdwBhAHIAZQAgAHMAaABhAGwAbAAgAG4AbwB0ACAAYgBlACAAdQBzAGUAZAAgAHQAbwAgAHAAcgBvAG0AbwB0AGUALAAgAGUAbgBkAG8AcgBzAGUAIABvAHIAIABhAGQAdgBlAHIAdABpAHMAZQAgAGEAbgB5AAoATQBvAGQAaQBmAGkAZQBkACAAVgBlAHIAcwBpAG8AbgAsACAAZQB4AGMAZQBwAHQAIAB0AG8AIABhAGMAawBuAG8AdwBsAGUAZABnAGUAIAB0AGgAZQAgAGMAbwBuAHQAcgBpAGIAdQB0AGkAbwBuACgAcwApACAAbwBmACAAdABoAGUACgBDAG8AcAB5AHIAaQBnAGgAdAAgAEgAbwBsAGQAZQByACgAcwApACAAYQBuAGQAIAB0AGgAZQAgAEEAdQB0AGgAbwByACgAcwApACAAbwByACAAdwBpAHQAaAAgAHQAaABlAGkAcgAgAGUAeABwAGwAaQBjAGkAdAAgAHcAcgBpAHQAdABlAG4ACgBwAGUAcgBtAGkAcwBzAGkAbwBuAC4ACgAKADUAKQAgAFQAaABlACAARgBvAG4AdAAgAFMAbwBmAHQAdwBhAHIAZQAsACAAbQBvAGQAaQBmAGkAZQBkACAAbwByACAAdQBuAG0AbwBkAGkAZgBpAGUAZAAsACAAaQBuACAAcABhAHIAdAAgAG8AcgAgAGkAbgAgAHcAaABvAGwAZQAsAAoAbQB1AHMAdAAgAGIAZQAgAGQAaQBzAHQAcgBpAGIAdQB0AGUAZAAgAGUAbgB0AGkAcgBlAGwAeQAgAHUAbgBkAGUAcgAgAHQAaABpAHMAIABsAGkAYwBlAG4AcwBlACwAIABhAG4AZAAgAG0AdQBzAHQAIABuAG8AdAAgAGIAZQAKAGQAaQBzAHQAcgBpAGIAdQB0AGUAZAAgAHUAbgBkAGUAcgAgAGEAbgB5ACAAbwB0AGgAZQByACAAbABpAGMAZQBuAHMAZQAuACAAVABoAGUAIAByAGUAcQB1AGkAcgBlAG0AZQBuAHQAIABmAG8AcgAgAGYAbwBuAHQAcwAgAHQAbwAKAHIAZQBtAGEAaQBuACAAdQBuAGQAZQByACAAdABoAGkAcwAgAGwAaQBjAGUAbgBzAGUAIABkAG8AZQBzACAAbgBvAHQAIABhAHAAcABsAHkAIAB0AG8AIABhAG4AeQAgAGQAbwBjAHUAbQBlAG4AdAAgAGMAcgBlAGEAdABlAGQACgB1AHMAaQBuAGcAIAB0AGgAZQAgAEYAbwBuAHQAIABTAG8AZgB0AHcAYQByAGUALgAKAAoAVABFAFIATQBJAE4AQQBUAEkATwBOAAoAVABoAGkAcwAgAGwAaQBjAGUAbgBzAGUAIABiAGUAYwBvAG0AZQBzACAAbgB1AGwAbAAgAGEAbgBkACAAdgBvAGkAZAAgAGkAZgAgAGEAbgB5ACAAbwBmACAAdABoAGUAIABhAGIAbwB2AGUAIABjAG8AbgBkAGkAdABpAG8AbgBzACAAYQByAGUACgBuAG8AdAAgAG0AZQB0AC4ACgAKAEQASQBTAEMATABBAEkATQBFAFIACgBUAEgARQAgAEYATwBOAFQAIABTAE8ARgBUAFcAQQBSAEUAIABJAFMAIABQAFIATwBWAEkARABFAEQAIAAiAEEAUwAgAEkAUwAiACwAIABXAEkAVABIAE8AVQBUACAAVwBBAFIAUgBBAE4AVABZACAATwBGACAAQQBOAFkAIABLAEkATgBEACwACgBFAFgAUABSAEUAUwBTACAATwBSACAASQBNAFAATABJAEUARAAsACAASQBOAEMATABVAEQASQBOAEcAIABCAFUAVAAgAE4ATwBUACAATABJAE0ASQBUAEUARAAgAFQATwAgAEEATgBZACAAVwBBAFIAUgBBAE4AVABJAEUAUwAgAE8ARgAKAE0ARQBSAEMASABBAE4AVABBAEIASQBMAEkAVABZACwAIABGAEkAVABOAEUAUwBTACAARgBPAFIAIABBACAAUABBAFIAVABJAEMAVQBMAEEAUgAgAFAAVQBSAFAATwBTAEUAIABBAE4ARAAgAE4ATwBOAEkATgBGAFIASQBOAEcARQBNAEUATgBUAAoATwBGACAAQwBPAFAAWQBSAEkARwBIAFQALAAgAFAAQQBUAEUATgBUACwAIABUAFIAQQBEAEUATQBBAFIASwAsACAATwBSACAATwBUAEgARQBSACAAUgBJAEcASABUAC4AIABJAE4AIABOAE8AIABFAFYARQBOAFQAIABTAEgAQQBMAEwAIABUAEgARQAKAEMATwBQAFkAUgBJAEcASABUACAASABPAEwARABFAFIAIABCAEUAIABMAEkAQQBCAEwARQAgAEYATwBSACAAQQBOAFkAIABDAEwAQQBJAE0ALAAgAEQAQQBNAEEARwBFAFMAIABPAFIAIABPAFQASABFAFIAIABMAEkAQQBCAEkATABJAFQAWQAsAAoASQBOAEMATABVAEQASQBOAEcAIABBAE4AWQAgAEcARQBOAEUAUgBBAEwALAAgAFMAUABFAEMASQBBAEwALAAgAEkATgBEAEkAUgBFAEMAVAAsACAASQBOAEMASQBEAEUATgBUAEEATAAsACAATwBSACAAQwBPAE4AUwBFAFEAVQBFAE4AVABJAEEATAAKAEQAQQBNAEEARwBFAFMALAAgAFcASABFAFQASABFAFIAIABJAE4AIABBAE4AIABBAEMAVABJAE8ATgAgAE8ARgAgAEMATwBOAFQAUgBBAEMAVAAsACAAVABPAFIAVAAgAE8AUgAgAE8AVABIAEUAUgBXAEkAUwBFACwAIABBAFIASQBTAEkATgBHAAoARgBSAE8ATQAsACAATwBVAFQAIABPAEYAIABUAEgARQAgAFUAUwBFACAATwBSACAASQBOAEEAQgBJAEwASQBUAFkAIABUAE8AIABVAFMARQAgAFQASABFACAARgBPAE4AVAAgAFMATwBGAFQAVwBBAFIARQAgAE8AUgAgAEYAUgBPAE0ACgBPAFQASABFAFIAIABEAEUAQQBMAEkATgBHAFMAIABJAE4AIABUAEgARQAgAEYATwBOAFQAIABTAE8ARgBUAFcAQQBSAEUALgAAQ29weXJpZ2h0IChjKSAyMDExLCBDb2R5ICJDb2RlTWFuMzgiIEJvaXNjbGFpciAoY29keUB6b25lMzgubmV0KSwKd2l0aCBSZXNlcnZlZCBGb250IE5hbWUgUHJlc3MgU3RhcnQuCgpUaGlzIEZvbnQgU29mdHdhcmUgaXMgbGljZW5zZWQgdW5kZXIgdGhlIFNJTCBPcGVuIEZvbnQgTGljZW5zZSwgVmVyc2lvbiAxLjEuClRoaXMgbGljZW5zZSBpcyBjb3BpZWQgYmVsb3csIGFuZCBpcyBhbHNvIGF2YWlsYWJsZSB3aXRoIGEgRkFRIGF0OgpodHRwOi8vc2NyaXB0cy5zaWwub3JnL09GTAoKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClNJTCBPUEVOIEZPTlQgTElDRU5TRSBWZXJzaW9uIDEuMSAtIDI2IEZlYnJ1YXJ5IDIwMDcKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KClBSRUFNQkxFClRoZSBnb2FscyBvZiB0aGUgT3BlbiBGb250IExpY2Vuc2UgKE9GTCkgYXJlIHRvIHN0aW11bGF0ZSB3b3JsZHdpZGUKZGV2ZWxvcG1lbnQgb2YgY29sbGFib3JhdGl2ZSBmb250IHByb2plY3RzLCB0byBzdXBwb3J0IHRoZSBmb250IGNyZWF0aW9uCmVmZm9ydHMgb2YgYWNhZGVtaWMgYW5kIGxpbmd1aXN0aWMgY29tbXVuaXRpZXMsIGFuZCB0byBwcm92aWRlIGEgZnJlZSBhbmQKb3BlbiBmcmFtZXdvcmsgaW4gd2hpY2ggZm9udHMgbWF5IGJlIHNoYXJlZCBhbmQgaW1wcm92ZWQgaW4gcGFydG5lcnNoaXAKd2l0aCBvdGhlcnMuCgpUaGUgT0ZMIGFsbG93cyB0aGUgbGljZW5zZWQgZm9udHMgdG8gYmUgdXNlZCwgc3R1ZGllZCwgbW9kaWZpZWQgYW5kCnJlZGlzdHJpYnV0ZWQgZnJlZWx5IGFzIGxvbmcgYXMgdGhleSBhcmUgbm90IHNvbGQgYnkgdGhlbXNlbHZlcy4gVGhlCmZvbnRzLCBpbmNsdWRpbmcgYW55IGRlcml2YXRpdmUgd29ya3MsIGNhbiBiZSBidW5kbGVkLCBlbWJlZGRlZCwgCnJlZGlzdHJpYnV0ZWQgYW5kL29yIHNvbGQgd2l0aCBhbnkgc29mdHdhcmUgcHJvdmlkZWQgdGhhdCBhbnkgcmVzZXJ2ZWQKbmFtZXMgYXJlIG5vdCB1c2VkIGJ5IGRlcml2YXRpdmUgd29ya3MuIFRoZSBmb250cyBhbmQgZGVyaXZhdGl2ZXMsCmhvd2V2ZXIsIGNhbm5vdCBiZSByZWxlYXNlZCB1bmRlciBhbnkgb3RoZXIgdHlwZSBvZiBsaWNlbnNlLiBUaGUKcmVxdWlyZW1lbnQgZm9yIGZvbnRzIHRvIHJlbWFpbiB1bmRlciB0aGlzIGxpY2Vuc2UgZG9lcyBub3QgYXBwbHkKdG8gYW55IGRvY3VtZW50IGNyZWF0ZWQgdXNpbmcgdGhlIGZvbnRzIG9yIHRoZWlyIGRlcml2YXRpdmVzLgoKREVGSU5JVElPTlMKIkZvbnQgU29mdHdhcmUiIHJlZmVycyB0byB0aGUgc2V0IG9mIGZpbGVzIHJlbGVhc2VkIGJ5IHRoZSBDb3B5cmlnaHQKSG9sZGVyKHMpIHVuZGVyIHRoaXMgbGljZW5zZSBhbmQgY2xlYXJseSBtYXJrZWQgYXMgc3VjaC4gVGhpcyBtYXkKaW5jbHVkZSBzb3VyY2UgZmlsZXMsIGJ1aWxkIHNjcmlwdHMgYW5kIGRvY3VtZW50YXRpb24uCgoiUmVzZXJ2ZWQgRm9udCBOYW1lIiByZWZlcnMgdG8gYW55IG5hbWVzIHNwZWNpZmllZCBhcyBzdWNoIGFmdGVyIHRoZQpjb3B5cmlnaHQgc3RhdGVtZW50KHMpLgoKIk9yaWdpbmFsIFZlcnNpb24iIHJlZmVycyB0byB0aGUgY29sbGVjdGlvbiBvZiBGb250IFNvZnR3YXJlIGNvbXBvbmVudHMgYXMKZGlzdHJpYnV0ZWQgYnkgdGhlIENvcHlyaWdodCBIb2xkZXIocykuCgoiTW9kaWZpZWQgVmVyc2lvbiIgcmVmZXJzIHRvIGFueSBkZXJpdmF0aXZlIG1hZGUgYnkgYWRkaW5nIHRvLCBkZWxldGluZywKb3Igc3Vic3RpdHV0aW5nIC0tIGluIHBhcnQgb3IgaW4gd2hvbGUgLS0gYW55IG9mIHRoZSBjb21wb25lbnRzIG9mIHRoZQpPcmlnaW5hbCBWZXJzaW9uLCBieSBjaGFuZ2luZyBmb3JtYXRzIG9yIGJ5IHBvcnRpbmcgdGhlIEZvbnQgU29mdHdhcmUgdG8gYQpuZXcgZW52aXJvbm1lbnQuCgoiQXV0aG9yIiByZWZlcnMgdG8gYW55IGRlc2lnbmVyLCBlbmdpbmVlciwgcHJvZ3JhbW1lciwgdGVjaG5pY2FsCndyaXRlciBvciBvdGhlciBwZXJzb24gd2hvIGNvbnRyaWJ1dGVkIHRvIHRoZSBGb250IFNvZnR3YXJlLgoKUEVSTUlTU0lPTiAmIENPTkRJVElPTlMKUGVybWlzc2lvbiBpcyBoZXJlYnkgZ3JhbnRlZCwgZnJlZSBvZiBjaGFyZ2UsIHRvIGFueSBwZXJzb24gb2J0YWluaW5nCmEgY29weSBvZiB0aGUgRm9udCBTb2Z0d2FyZSwgdG8gdXNlLCBzdHVkeSwgY29weSwgbWVyZ2UsIGVtYmVkLCBtb2RpZnksCnJlZGlzdHJpYnV0ZSwgYW5kIHNlbGwgbW9kaWZpZWQgYW5kIHVubW9kaWZpZWQgY29waWVzIG9mIHRoZSBGb250ClNvZnR3YXJlLCBzdWJqZWN0IHRvIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uczoKCjEpIE5laXRoZXIgdGhlIEZvbnQgU29mdHdhcmUgbm9yIGFueSBvZiBpdHMgaW5kaXZpZHVhbCBjb21wb25lbnRzLAppbiBPcmlnaW5hbCBvciBNb2RpZmllZCBWZXJzaW9ucywgbWF5IGJlIHNvbGQgYnkgaXRzZWxmLgoKMikgT3JpZ2luYWwgb3IgTW9kaWZpZWQgVmVyc2lvbnMgb2YgdGhlIEZvbnQgU29mdHdhcmUgbWF5IGJlIGJ1bmRsZWQsCnJlZGlzdHJpYnV0ZWQgYW5kL29yIHNvbGQgd2l0aCBhbnkgc29mdHdhcmUsIHByb3ZpZGVkIHRoYXQgZWFjaCBjb3B5CmNvbnRhaW5zIHRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlIGFuZCB0aGlzIGxpY2Vuc2UuIFRoZXNlIGNhbiBiZQppbmNsdWRlZCBlaXRoZXIgYXMgc3RhbmQtYWxvbmUgdGV4dCBmaWxlcywgaHVtYW4tcmVhZGFibGUgaGVhZGVycyBvcgppbiB0aGUgYXBwcm9wcmlhdGUgbWFjaGluZS1yZWFkYWJsZSBtZXRhZGF0YSBmaWVsZHMgd2l0aGluIHRleHQgb3IKYmluYXJ5IGZpbGVzIGFzIGxvbmcgYXMgdGhvc2UgZmllbGRzIGNhbiBiZSBlYXNpbHkgdmlld2VkIGJ5IHRoZSB1c2VyLgoKMykgTm8gTW9kaWZpZWQgVmVyc2lvbiBvZiB0aGUgRm9udCBTb2Z0d2FyZSBtYXkgdXNlIHRoZSBSZXNlcnZlZCBGb250Ck5hbWUocykgdW5sZXNzIGV4cGxpY2l0IHdyaXR0ZW4gcGVybWlzc2lvbiBpcyBncmFudGVkIGJ5IHRoZSBjb3JyZXNwb25kaW5nCkNvcHlyaWdodCBIb2xkZXIuIFRoaXMgcmVzdHJpY3Rpb24gb25seSBhcHBsaWVzIHRvIHRoZSBwcmltYXJ5IGZvbnQgbmFtZSBhcwpwcmVzZW50ZWQgdG8gdGhlIHVzZXJzLgoKNCkgVGhlIG5hbWUocykgb2YgdGhlIENvcHlyaWdodCBIb2xkZXIocykgb3IgdGhlIEF1dGhvcihzKSBvZiB0aGUgRm9udApTb2Z0d2FyZSBzaGFsbCBub3QgYmUgdXNlZCB0byBwcm9tb3RlLCBlbmRvcnNlIG9yIGFkdmVydGlzZSBhbnkKTW9kaWZpZWQgVmVyc2lvbiwgZXhjZXB0IHRvIGFja25vd2xlZGdlIHRoZSBjb250cmlidXRpb24ocykgb2YgdGhlCkNvcHlyaWdodCBIb2xkZXIocykgYW5kIHRoZSBBdXRob3Iocykgb3Igd2l0aCB0aGVpciBleHBsaWNpdCB3cml0dGVuCnBlcm1pc3Npb24uCgo1KSBUaGUgRm9udCBTb2Z0d2FyZSwgbW9kaWZpZWQgb3IgdW5tb2RpZmllZCwgaW4gcGFydCBvciBpbiB3aG9sZSwKbXVzdCBiZSBkaXN0cmlidXRlZCBlbnRpcmVseSB1bmRlciB0aGlzIGxpY2Vuc2UsIGFuZCBtdXN0IG5vdCBiZQpkaXN0cmlidXRlZCB1bmRlciBhbnkgb3RoZXIgbGljZW5zZS4gVGhlIHJlcXVpcmVtZW50IGZvciBmb250cyB0bwpyZW1haW4gdW5kZXIgdGhpcyBsaWNlbnNlIGRvZXMgbm90IGFwcGx5IHRvIGFueSBkb2N1bWVudCBjcmVhdGVkCnVzaW5nIHRoZSBGb250IFNvZnR3YXJlLgoKVEVSTUlOQVRJT04KVGhpcyBsaWNlbnNlIGJlY29tZXMgbnVsbCBhbmQgdm9pZCBpZiBhbnkgb2YgdGhlIGFib3ZlIGNvbmRpdGlvbnMgYXJlCm5vdCBtZXQuCgpESVNDTEFJTUVSClRIRSBGT05UIFNPRlRXQVJFIElTIFBST1ZJREVEICJBUyBJUyIsIFdJVEhPVVQgV0FSUkFOVFkgT0YgQU5ZIEtJTkQsCkVYUFJFU1MgT1IgSU1QTElFRCwgSU5DTFVESU5HIEJVVCBOT1QgTElNSVRFRCBUTyBBTlkgV0FSUkFOVElFUyBPRgpNRVJDSEFOVEFCSUxJVFksIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFIEFORCBOT05JTkZSSU5HRU1FTlQKT0YgQ09QWVJJR0hULCBQQVRFTlQsIFRSQURFTUFSSywgT1IgT1RIRVIgUklHSFQuIElOIE5PIEVWRU5UIFNIQUxMIFRIRQpDT1BZUklHSFQgSE9MREVSIEJFIExJQUJMRSBGT1IgQU5ZIENMQUlNLCBEQU1BR0VTIE9SIE9USEVSIExJQUJJTElUWSwKSU5DTFVESU5HIEFOWSBHRU5FUkFMLCBTUEVDSUFMLCBJTkRJUkVDVCwgSU5DSURFTlRBTCwgT1IgQ09OU0VRVUVOVElBTApEQU1BR0VTLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcKRlJPTSwgT1VUIE9GIFRIRSBVU0UgT1IgSU5BQklMSVRZIFRPIFVTRSBUSEUgRk9OVCBTT0ZUV0FSRSBPUiBGUk9NCk9USEVSIERFQUxJTkdTIElOIFRIRSBGT05UIFNPRlRXQVJFLgAAaAB0AHQAcAA6AC8ALwBzAGMAcgBpAHAAdABzAC4AcwBpAGwALgBvAHIAZwAvAE8ARgBMAABodHRwOi8vc2NyaXB0cy5zaWwub3JnL09GTAAAAAACAAAAAAAA/+kAMwAAAAEAAAAAAAAAAAAAAAAAAAAAAi8AAAABAAIAAwAEAAUABgAHAAgACQAKAAsADAANAA4ADwAQABEAEgATABQAFQAWABcAGAAZABoAGwAcAB0AHgAfACAAIQAiACMAJAAlACYAJwAoACkAKgArACwALQAuAC8AMAAxADIAMwA0ADUANgA3ADgAOQA6ADsAPAA9AD4APwBAAEEAQgBDAEQARQBGAEcASABJAEoASwBMAE0ATgBPAFAAUQBSAFMAVABVAFYAVwBYAFkAWgBbAFwAXQBeAF8AYABhAQIBAwCjAIQAhQC9AJYA6ACGAI4AiwCdAKkApAEEAIoA2gCDAJMBBQEGAI0BBwCIAMMA3gEIAJ4AqgD1APQA9gCiAK0AyQDHAK4AYgBjAJAAZADLAGUAyADKAM8AzADNAM4A6QBmANMA0ADRAK8AZwDwAJEA1gDUANUAaADrAO0AiQBqAGkAawBtAGwAbgCgAG8AcQBwAHIAcwB1AHQAdgB3AOoAeAB6AHkAewB9AHwAuAChAH8AfgCAAIEA7ADuALoBCQEKAQsBDAENAQ4A/QD+AQ8BEAERARIA/wEAARMBFAEVAQEBFgEXARgBGQEaARsBHAEdAR4BHwEgASEA+AD5ASIBIwEkASUBJgEnASgBKQEqASsBLAEtAS4BLwEwATEA+gDXATIBMwE0ATUBNgE3ATgBOQE6ATsBPAE9AT4BPwFAAOIA4wFBAUIBQwFEAUUBRgFHAUgBSQFKAUsBTAFNAU4BTwCwALEBUAFRAVIBUwFUAVUBVgFXAVgBWQD7APwA5ADlAVoBWwFcAV0BXgFfAWABYQFiAWMBZAFlAWYBZwFoAWkBagFrAWwBbQFuAW8AuwFwAXEBcgFzAOYA5wF0AKYA2ADhAXUBdgF3AXgA2wDcAN0A4ADZAN8BeQF6AXsBfAF9AX4BfwGAAYEBggGDAYQBhQGGAYcBiACoAYkBigGLAYwBjQGOAY8BkAGRAZIBkwGUAZUBlgGXAZgBmQGaAZsAnwGcAZ0BngGfAaABoQGiAaMBpAGlAaYBpwGoAakBqgGrAawBrQCXAa4BrwGwAJsBsQGyAbMBtAG1AbYBtwG4AbkBugG7AbwBvQG+Ab8BwAHBAcIBwwHEAcUBxgHHAcgByQHKAcsBzAHNAc4BzwHQAdEB0gHTAdQB1QHWAdcB2AHZAdoB2wHcAd0B3gHfAeAB4QHiAeMB5AHlAeYB5wHoAekB6gHrAewB7QHuAe8B8AHxAfIB8wH0AfUB9gH3AfgB+QH6AfsB/AH9Af4B/wIAAgECAgIDAgQCBQIGAgcCCAIJAgoCCwIMAg0CDgIPAhACEQISAhMCFAIVAhYCFwIYAhkCGgIbAhwCHQIeALIAswIfALYAtwDEALQAtQDFAIIAwgCHAKsAxgC+AL8AvAIgAiECIgCMAiMCJAIlAiYAmAInAJoAmQClAJIAnACnAI8AlACVAigCKQIqAisAuQIsAi0CLgIvAjACMQIyAjMCNAI1AjYCNwd1bmkwMDdGB3VuaTAwQTAHdW5pMDBBRAd1bmkwMEIyB3VuaTAwQjMHdW5pMDBCNQd1bmkwMEI5B0FtYWNyb24HYW1hY3JvbgZBYnJldmUGYWJyZXZlB0FvZ29uZWsHYW9nb25lawtDY2lyY3VtZmxleAtjY2lyY3VtZmxleApDZG90YWNjZW50CmNkb3RhY2NlbnQGRGNhcm9uBmRjYXJvbgZEY3JvYXQHRW1hY3JvbgdlbWFjcm9uBkVicmV2ZQZlYnJldmUKRWRvdGFjY2VudAplZG90YWNjZW50B0VvZ29uZWsHZW9nb25lawZFY2Fyb24GZWNhcm9uC0djaXJjdW1mbGV4C2djaXJjdW1mbGV4Ckdkb3RhY2NlbnQKZ2RvdGFjY2VudAxHY29tbWFhY2NlbnQMZ2NvbW1hYWNjZW50C0hjaXJjdW1mbGV4C2hjaXJjdW1mbGV4BEhiYXIEaGJhcgZJdGlsZGUGaXRpbGRlB0ltYWNyb24HaW1hY3JvbgZJYnJldmUGaWJyZXZlB0lvZ29uZWsHaW9nb25lawJJSgJpagtKY2lyY3VtZmxleAtqY2lyY3VtZmxleAxLY29tbWFhY2NlbnQMa2NvbW1hYWNjZW50DGtncmVlbmxhbmRpYwZMYWN1dGUGbGFjdXRlDExjb21tYWFjY2VudAxsY29tbWFhY2NlbnQGTGNhcm9uBmxjYXJvbgRMZG90BGxkb3QGTmFjdXRlBm5hY3V0ZQxOY29tbWFhY2NlbnQMbmNvbW1hYWNjZW50Bk5jYXJvbgZuY2Fyb24LbmFwb3N0cm9waGUDRW5nA2VuZwdPbWFjcm9uB29tYWNyb24GT2JyZXZlBm9icmV2ZQ1PaHVuZ2FydW1sYXV0DW9odW5nYXJ1bWxhdXQGUmFjdXRlBnJhY3V0ZQxSY29tbWFhY2NlbnQMcmNvbW1hYWNjZW50BlJjYXJvbgZyY2Fyb24GU2FjdXRlBnNhY3V0ZQtTY2lyY3VtZmxleAtzY2lyY3VtZmxleAxUY29tbWFhY2NlbnQMdGNvbW1hYWNjZW50BlRjYXJvbgZ0Y2Fyb24EVGJhcgR0YmFyBlV0aWxkZQZ1dGlsZGUHVW1hY3Jvbgd1bWFjcm9uBlVicmV2ZQZ1YnJldmUFVXJpbmcFdXJpbmcNVWh1bmdhcnVtbGF1dA11aHVuZ2FydW1sYXV0B1VvZ29uZWsHdW9nb25lawtXY2lyY3VtZmxleAt3Y2lyY3VtZmxleAtZY2lyY3VtZmxleAt5Y2lyY3VtZmxleAZaYWN1dGUGemFjdXRlClpkb3RhY2NlbnQKemRvdGFjY2VudAVsb25ncwd1bmkwMkM5B3VuaTAyQ0EHdW5pMDJDQgd1bmkwMkQ3B3VuaTAzN0EHdW5pMDM3RQV0b25vcw1kaWVyZXNpc3Rvbm9zCkFscGhhdG9ub3MJYW5vdGVsZWlhDEVwc2lsb250b25vcwhFdGF0b25vcwlJb3RhdG9ub3MMT21pY3JvbnRvbm9zDFVwc2lsb250b25vcwpPbWVnYXRvbm9zEWlvdGFkaWVyZXNpc3Rvbm9zBUFscGhhBEJldGEFR2FtbWEHRXBzaWxvbgRaZXRhA0V0YQVUaGV0YQRJb3RhBUthcHBhBkxhbWJkYQJNdQJOdQJYaQdPbWljcm9uAlBpA1JobwVTaWdtYQNUYXUHVXBzaWxvbgNQaGkDQ2hpA1BzaQxJb3RhZGllcmVzaXMPVXBzaWxvbmRpZXJlc2lzCmFscGhhdG9ub3MMZXBzaWxvbnRvbm9zCGV0YXRvbm9zCWlvdGF0b25vcxR1cHNpbG9uZGllcmVzaXN0b25vcwVhbHBoYQRiZXRhBWdhbW1hBWRlbHRhB2Vwc2lsb24EemV0YQNldGEFdGhldGEEaW90YQVrYXBwYQZsYW1iZGECbnUCeGkHb21pY3JvbgNyaG8Gc2lnbWExBXNpZ21hA3RhdQd1cHNpbG9uA3BoaQNjaGkDcHNpBW9tZWdhDGlvdGFkaWVyZXNpcw91cHNpbG9uZGllcmVzaXMMb21pY3JvbnRvbm9zDHVwc2lsb250b25vcwpvbWVnYXRvbm9zB3VuaTA0MDAJYWZpaTEwMDIzCWFmaWkxMDA1MQlhZmlpMTAwNTIJYWZpaTEwMDUzCWFmaWkxMDA1NAlhZmlpMTAwNTUJYWZpaTEwMDU2CWFmaWkxMDA1NwlhZmlpMTAwNTgJYWZpaTEwMDU5CWFmaWkxMDA2MAlhZmlpMTAwNjEHdW5pMDQwRAlhZmlpMTAwNjIJYWZpaTEwMTQ1CWFmaWkxMDAxNwlhZmlpMTAwMTgJYWZpaTEwMDE5CWFmaWkxMDAyMAlhZmlpMTAwMjEJYWZpaTEwMDIyCWFmaWkxMDAyNAlhZmlpMTAwMjUJYWZpaTEwMDI2CWFmaWkxMDAyNwlhZmlpMTAwMjgJYWZpaTEwMDI5CWFmaWkxMDAzMAlhZmlpMTAwMzEJYWZpaTEwMDMyCWFmaWkxMDAzMwlhZmlpMTAwMzQJYWZpaTEwMDM1CWFmaWkxMDAzNglhZmlpMTAwMzcJYWZpaTEwMDM4CWFmaWkxMDAzOQlhZmlpMTAwNDAJYWZpaTEwMDQxCWFmaWkxMDA0MglhZmlpMTAwNDMJYWZpaTEwMDQ0CWFmaWkxMDA0NQlhZmlpMTAwNDYJYWZpaTEwMDQ3CWFmaWkxMDA0OAlhZmlpMTAwNDkJYWZpaTEwMDY1CWFmaWkxMDA2NglhZmlpMTAwNjcJYWZpaTEwMDY4CWFmaWkxMDA2OQlhZmlpMTAwNzAJYWZpaTEwMDcyCWFmaWkxMDA3MwlhZmlpMTAwNzQJYWZpaTEwMDc1CWFmaWkxMDA3NglhZmlpMTAwNzcJYWZpaTEwMDc4CWFmaWkxMDA3OQlhZmlpMTAwODAJYWZpaTEwMDgxCWFmaWkxMDA4MglhZmlpMTAwODMJYWZpaTEwMDg0CWFmaWkxMDA4NQlhZmlpMTAwODYJYWZpaTEwMDg3CWFmaWkxMDA4OAlhZmlpMTAwODkJYWZpaTEwMDkwCWFmaWkxMDA5MQlhZmlpMTAwOTIJYWZpaTEwMDkzCWFmaWkxMDA5NAlhZmlpMTAwOTUJYWZpaTEwMDk2CWFmaWkxMDA5Nwd1bmkwNDUwCWFmaWkxMDA3MQlhZmlpMTAwOTkJYWZpaTEwMTAwCWFmaWkxMDEwMQlhZmlpMTAxMDIJYWZpaTEwMTAzCWFmaWkxMDEwNAlhZmlpMTAxMDUJYWZpaTEwMTA2CWFmaWkxMDEwNwlhZmlpMTAxMDgJYWZpaTEwMTA5B3VuaTA0NUQJYWZpaTEwMTEwCWFmaWkxMDE5MwlhZmlpMDAyMDgERXVybwd1bmkyMEFGCWFmaWk2MTM1MglhcnJvd2xlZnQHYXJyb3d1cAphcnJvd3JpZ2h0CWFycm93ZG93bgd1bmkyMjA2B3RyaWFndXAHdW5pMjVCNgd0cmlhZ2RuB3VuaTI1QzAHdW5pMjYwNQd1bmkyNjA2BXNwYWRlBGNsdWIFaGVhcnQHZGlhbW9uZAttdXNpY2Fsbm90ZQd1bmlGMTAwB3VuaUYxMDEHdW5pRjhGRgd1bmlGQjAxB3VuaUZCMDIAAAAAAf//AAIAAQAAAA4AAAAYACAAAAACAAEAAQIuAAEABAAAAAIAAAABAAAAAQAAAAAAAQAAAADMPaLPAAAAAMobHN4AAAAAzLYldw==","spacing":0},"bevel":{"blurX":4,"blurY":4,"angle":45,"strength":1,"highlightColor":16777215,"type":"inner","bevelEnabled":false,"highlightAlpha":1,"distance":4,"shadowColor":0,"shadowAlpha":1,"quality":1},"stroke":{"textureScale":1,"gradientAlphas":[1,1],"miterLimit":1,"strokeEnabled":true,"pixelHinting":false,"jointStyle":"miter","xOffset":0,"gradientColors":[7302764,4868681],"yOffset":0,"gradientRatios":[0,255],"gradientRotation":90,"gradientType":"linear","size":1,"fillType":"gradientFill"},"glow":{"blurX":2,"ratios":[0,255],"distance":0,"blurY":2,"angle":45,"colors":[16711680,16711680],"strength":2,"glowEnabled":false,"quality":3,"alphas":[0,1]},"shadow":{"blurX":4,"alpha":1,"blurY":4,"angle":45,"strength":1,"shadowEnabled":false,"distance":4,"quality":1,"color":0},"glyphs":{"descriptionFormat":1,"canvasWidth":"128","roundValues":true,"canvasHeight":"128","glyphs":" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~","packMethod":1,"padding":1,"powerOfTwo":true},"fill":{"distanceFieldColor":16777215,"textureScale":1,"distanceFieldEnabled":false,"distanceFieldDownscale":4,"distanceFieldSpread":16,"xOffset":0,"distanceFieldType":"Type 1","gradientColors":[16740096,16514041,16513783,16777215],"yOffset":0,"gradientRatios":[0,0,0,255],"gradientRotation":90,"gradientType":"radial","gradientAlphas":[1,1,1,1],"fillType":"gradientFill"},"settings":{"filename":"PressStart2P","scalings":"1;","postfixes":""},"background":{"alpha":0,"color":16777215}} \ No newline at end of file diff --git a/plugins/debug/font/PressStart2P.png b/plugins/debug/font/PressStart2P.png new file mode 100755 index 0000000000000000000000000000000000000000..8f63bf33d1f06ee5aac4eb6806265f6ae0e918ad GIT binary patch literal 7672 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 mE@^FHXJsx>PDe5{MQ&qnWMy)w27m4V0000 Date: Sun, 27 Nov 2016 18:48:10 +0800 Subject: [PATCH 170/177] [#593] Fix the particle debug panel in webgl mode this one has very little text, so i keep like this for now. for the display color issue, see https://github.com/melonjs/melonJS/issues/841 --- plugins/debug/particleDebugPanel.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/debug/particleDebugPanel.js b/plugins/debug/particleDebugPanel.js index ac3774f4b1..f330a20bc7 100644 --- a/plugins/debug/particleDebugPanel.js +++ b/plugins/debug/particleDebugPanel.js @@ -177,10 +177,9 @@ var maxTime = 60, scale = height / maxTime, len = updateTimeSamples.length; var frameTimeLimit = 1000 / me.sys.fps, where = height - frameTimeLimit * scale; - renderer.setGlobalAlpha(0.5); - renderer.setColor("grey"); + renderer.setColor("#80808080"); renderer.strokeLine(0, where, width, where); - renderer.setGlobalAlpha(1.0); + renderer.setColor("#FFFFFFFF"); var updateTimeSum = 0, drawTimeSum = 0, frameUpdateTimeSum = 0, frameDrawTimeSum = 0, update = [], slowUpdate = [], draw = [], slowDraw = []; @@ -241,13 +240,14 @@ renderer.save(); // draw the panel - renderer.setGlobalAlpha(0.5); - renderer.setColor("black"); + renderer.setColor("#00000080"); + renderer.fillRect(this._bounds.left, this._bounds.top, this._bounds.width, this._bounds.height); - renderer.setGlobalAlpha(1.0); renderer.translate(this._bounds.left, this._bounds.top); + renderer.setColor("#FFFFFFFF"); + // # entities / draw this.font.draw(renderer, "emitters : " + this.emitterCount, 5, 5); this.font.draw(renderer, "particles : " + this.particleCount, 5, 18); From 6f3b4909c115619a985ee0237706beb3eaf3e761 Mon Sep 17 00:00:00 2001 From: Olivier Biot Date: Sun, 27 Nov 2016 18:53:04 +0800 Subject: [PATCH 171/177] prevent eslint from parsing non .js file from the debug folder --- Gruntfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gruntfile.js b/Gruntfile.js index 07b2109414..3c05d5f14b 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -152,7 +152,7 @@ module.exports = function (grunt) { testSpecs, sourceFiles, "Gruntfile.js", - "plugins/**/*" + "plugins/**/*.js" ] } }, From 01b4494d7be7fb98593ef775b1d06b8d452b3247 Mon Sep 17 00:00:00 2001 From: Olivier Biot Date: Sun, 27 Nov 2016 19:43:24 +0800 Subject: [PATCH 172/177] [#637] rename the `debug` flag to `verbose` it is a much more appropriate name for it. I also moved it to the base renderer class, as it can be used as well to display more information on the webgl or canvas renderer. --- src/video/renderer.js | 2 ++ src/video/video.js | 2 +- src/video/webgl/compositor.js | 2 +- src/video/webgl/webgl_renderer.js | 3 --- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/video/renderer.js b/src/video/renderer.js index c661251a51..66c5620ae0 100644 --- a/src/video/renderer.js +++ b/src/video/renderer.js @@ -21,6 +21,7 @@ * @param {Boolean} [options.antiAlias=false] Whether to enable anti-aliasing * @param {Boolean} [options.transparent=false] Whether to enable transparency on the canvas (performance hit when enabled) * @param {Boolean} [options.subPixel=false] Whether to enable subpixel rendering (performance hit when enabled) + * @param {Boolean} [options.verbose=false] Enable the verbose mode that provides additional details as to what the renderer is doing * @param {Number} [options.zoomX=width] The actual width of the canvas with scaling applied * @param {Number} [options.zoomY=height] The actual height of the canvas with scaling applied */ @@ -38,6 +39,7 @@ this.doubleBuffering = !!(options.doubleBuffering); this.antiAlias = !!(options.antiAlias); this.subPixel = !!(options.subPixel); + this.verbose = !!(options.verbose); this.gameWidthZoom = options.zoomX || width; this.gameHeightZoom = options.zoomY || height; diff --git a/src/video/video.js b/src/video/video.js index b7543eae61..07b6d11676 100644 --- a/src/video/video.js +++ b/src/video/video.js @@ -39,7 +39,7 @@ transparent : false, antiAlias : false, subPixel : false, - debug : false + verbose : false }; diff --git a/src/video/webgl/compositor.js b/src/video/webgl/compositor.js index 68bc34e0d8..f4da9730cb 100644 --- a/src/video/webgl/compositor.js +++ b/src/video/webgl/compositor.js @@ -383,7 +383,7 @@ var region = texture.getRegion(key); if (typeof(region) === "undefined") { // TODO: Require proper atlas regions instead of caching arbitrary region keys - if (me.video.renderer.debug === true) { + if (me.video.renderer.verbose === true) { console.warn("Adding texture region", key, "for texture", texture); } diff --git a/src/video/webgl/webgl_renderer.js b/src/video/webgl/webgl_renderer.js index b1698187b2..96398aca4d 100644 --- a/src/video/webgl/webgl_renderer.js +++ b/src/video/webgl/webgl_renderer.js @@ -22,7 +22,6 @@ * @param {Boolean} [options.subPixel=false] Whether to enable subpixel renderering (performance hit when enabled) * @param {Number} [options.zoomX=width] The actual width of the canvas with scaling applied * @param {Number} [options.zoomY=height] The actual height of the canvas with scaling applied - * @param {Boolean} [options.debug=false] Enable the WebGL renderer debug mode (e.g. display warning messages about improrer usage) * @param {me.WebGLRenderer.Compositor} [options.compositor] A class that implements the compositor API */ me.WebGLRenderer = me.Renderer.extend( @@ -84,8 +83,6 @@ // Configure the WebGL viewport this.scaleCanvas(1, 1); - this.debug = !!(options.debug); - return this; }, From eff9d877f833e007860431782fd4dc6b0ea6113c Mon Sep 17 00:00:00 2001 From: obiot Date: Mon, 28 Nov 2016 10:26:38 +0800 Subject: [PATCH 173/177] CHANGELOG update --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 88060a1971..4a71f021b8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,7 +5,7 @@ Version History * Container : added a 'forEach' function that allows to iterate through all children * Core : largely improved and completed the matrix implementation and usage * Core : Object.assign polyfill update -* Core : various improvements and fixes to jasmine test units. +* Core : various improvements and fixes to jasmine test units * Font : added true support for bitmap font generated out of true type fonts (e.g. Littera, bmfont, bmglypgh, etc...) * Particles : reduced creation/use of temporary (vector) objects * Pointer : fixed bug with Chrome on Windows touch devices where touch input would register, but mouse input would not. (credit to BrianRosamilia) From ef36c6823f41eaf4cd1fd3edefdf09195e83ec86 Mon Sep 17 00:00:00 2001 From: obiot Date: Mon, 28 Nov 2016 10:26:51 +0800 Subject: [PATCH 174/177] CHANGELOG update --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 4a71f021b8..f32d2b9009 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,7 @@ Version History * Core : largely improved and completed the matrix implementation and usage * Core : Object.assign polyfill update * Core : various improvements and fixes to jasmine test units +* Debug : fixed the debug panel display when using the WebGL renderer * Font : added true support for bitmap font generated out of true type fonts (e.g. Littera, bmfont, bmglypgh, etc...) * Particles : reduced creation/use of temporary (vector) objects * Pointer : fixed bug with Chrome on Windows touch devices where touch input would register, but mouse input would not. (credit to BrianRosamilia) From 5a8b2dcecf656a46c61eaf4a1e449b2801b8da4d Mon Sep 17 00:00:00 2001 From: obiot Date: Mon, 28 Nov 2016 11:14:16 +0800 Subject: [PATCH 175/177] fix bitmapfont renderering under Firefox for "0 size" glyph (i.e. space character) FF does throw an exception in that case (in all fairness, as described in the API), so ..... --- src/font/bitmapfont.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/font/bitmapfont.js b/src/font/bitmapfont.js index 691ab7dfa0..411fd478bb 100644 --- a/src/font/bitmapfont.js +++ b/src/font/bitmapfont.js @@ -198,13 +198,16 @@ var kerning = (lastGlyph && lastGlyph.kerning) ? lastGlyph.getKerning(ch) : 0; // draw it - renderer.drawImage(this.fontImage, - glyph.src.x, glyph.src.y, - glyph.width, glyph.height, - x + glyph.offset.x, - y + glyph.offset.y * this.fontScale.y, - glyph.width * this.fontScale.x, glyph.height * this.fontScale.y - ); + if (glyph.width !== 0 && glyph.height !== 0) { + // some browser throw an exception when drawing a 0 width or height image + renderer.drawImage(this.fontImage, + glyph.src.x, glyph.src.y, + glyph.width, glyph.height, + x + glyph.offset.x, + y + glyph.offset.y * this.fontScale.y, + glyph.width * this.fontScale.x, glyph.height * this.fontScale.y + ); + } // increment position x += (glyph.xadvance + kerning) * this.fontScale.x; From bd6455e0c29ac25255adeeecfc19505edf86209f Mon Sep 17 00:00:00 2001 From: obiot Date: Mon, 28 Nov 2016 11:20:37 +0800 Subject: [PATCH 176/177] [#841][WebGL] multiply the new currentColor alpha with the previous globalAlpha value also restored the previous debugPanel setColor usage, as a testcase, and to align both the canvas and wegbl rendering. --- plugins/debug/debugPanel.js | 7 ++++--- src/video/webgl/webgl_renderer.js | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/debug/debugPanel.js b/plugins/debug/debugPanel.js index fee6783538..49ba246f75 100644 --- a/plugins/debug/debugPanel.js +++ b/plugins/debug/debugPanel.js @@ -590,13 +590,14 @@ } // draw the panel - renderer.setColor("#00000080"); + renderer.setGlobalAlpha(0.5); + renderer.setColor("black"); renderer.fillRect( this.left, this.top, this.width, this.height ); - - renderer.setColor("#FFFFFFFF"); + renderer.setGlobalAlpha(1.0); + renderer.setColor("white"); this.font.textAlign = "left"; diff --git a/src/video/webgl/webgl_renderer.js b/src/video/webgl/webgl_renderer.js index 96398aca4d..05081cfd53 100644 --- a/src/video/webgl/webgl_renderer.js +++ b/src/video/webgl/webgl_renderer.js @@ -514,7 +514,9 @@ * @param {me.Color|String} color css color string. */ setColor : function (color) { + var alpha = this.currentColor.glArray[3]; this.currentColor.copy(color); + this.currentColor.glArray[3] *= alpha; }, /** From b9082649dfa8aeea04bcb0f94058983df80d030d Mon Sep 17 00:00:00 2001 From: obiot Date: Mon, 28 Nov 2016 11:22:58 +0800 Subject: [PATCH 177/177] CHANGELOG update --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index f32d2b9009..7ee9fc76d5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -21,6 +21,7 @@ Version History * Shapes : polygon based shapes can now be freely transformed, and bounding box will be resized accordingly * TMX : scaling and rotation can now be applied to a tilemap (see me.levelDirector.loadLevel() for example) * TMX : fixed some issues when loading TMX level into a child container. +* WebGLRenderer : fixed the currentColor alpha not taking in account the "globalAlpha" (now better match canvas renderer) 3.1.0 * Audio : exposed a `me.audio.rate` function to allow modifying the playback rate of the specified audio clip.