diff --git a/includes/script.js b/includes/script.js index 10ee0fc..78323ad 100644 --- a/includes/script.js +++ b/includes/script.js @@ -1,6 +1,7 @@ $(document).ready(function() { // todo define things like 16x16, 128x128 etc. as constants? // also script debounce/throttle times + const animationTime = 400; // defined in bitsy.js var bitsyData = {}; @@ -27,6 +28,8 @@ $(document).ready(function() { var tiles = []; + var tileMatchThreshold = 0; + var croptions = { url: 'https://i.imgur.com/ThQZ94v.jpg', viewport: {width: 128, height: 128, type: 'square'}, @@ -87,6 +90,19 @@ $(document).ready(function() { return _.first(_.sortBy(colourOptions, 'difference')); } + function newTileName() { + var tileNames = _.map(bitsyData.tiles, 'name'); + + var i = 1; // start with 1 as 0 is an implicit tile + + while (tileNames.indexOf(i.toString(36)) > -1) { + i++; + } + + // base 36 = 0-9a-z + return i.toString(36); + } + function handleBitsyGameData() { bitsyData = {}; @@ -127,15 +143,16 @@ $(document).ready(function() { // get tiles - bitsyData.tiles = {}; + bitsyData.tiles = []; // tile 0 (background colour only) is implicit in bitsy rather than being stored in the game data // so, make our own version - bitsyData.tiles[0] = { - name: 0, - bitmap: _.chunk(_.split(_.repeat(0, 64), ''), 8) - }; - + bitsyData.tiles.push({ + name: "0", + bitmap: _.chunk(_.times(64, _.constant(0)), 8), + new: false // this could also be used to stop it from being added to the game data, wooo + }); + // todo: handle animated tiles properly instead of discarding the second animation frame var tiles = input.match(/TIL (.*)\n([01]{8}\n){8}(>\n([01]{8}\n){8})?/g); // everything after > is an optional second animation frame @@ -144,9 +161,14 @@ $(document).ready(function() { tile = tile.replace(/TIL .*\n/, ''); - var bitmap = tile.match(/[01]/g); + var bitmap = _.map(tile.match(/[01]/g), _.toInteger); - var newTile = {name: name}; + var newTile = { + name: name, + new: false + }; + + // todo make this agnostic? i.e. tile.frames = _.chunk(bitmap, 64) if (bitmap.length === 64) { // normal tile newTile.bitmap = _.chunk(bitmap, 8); @@ -155,7 +177,7 @@ $(document).ready(function() { newTile.secondAnimationFrame = _.chunk(_.takeRight(bitmap, 64), 8); } - bitsyData.tiles[name] = newTile; + bitsyData.tiles.push(newTile); }); if (_.find(bitsyData.palettes, {'id': palette.id})) { @@ -172,7 +194,7 @@ $(document).ready(function() { palette = _.first(_.sortBy(bitsyData.palettes, 'id')); } - renderResult(); + renderDebounced(); // update palette picker $('tr.palette').remove(); @@ -210,7 +232,7 @@ $(document).ready(function() { } } - var renderResult = _.debounce(function() { + function render() { $croppie.croppie('result', { type: 'rawcanvas', size: 'viewport' @@ -232,10 +254,10 @@ $(document).ready(function() { var targetColour = getClosestColour(pixel, palette); - if (targetColour.name === "background") { // ?! why is this reversed? - monochrome.push(1); + if (targetColour.name === "background") { + monochrome.push(0); } else { // tile - monochrome.push(0) + monochrome.push(1) } rawData[i ] = targetColour.red; @@ -262,23 +284,16 @@ $(document).ready(function() { pseudoTile.push( _.slice(monochrome[(tileY * 8) + y], (tileX * 8), (tileX * 8) + 8) ); - }) - + }); + var tilesForMatch = bitsyData.tiles; - _.each(tilesForMatch, function(tile) { - tile.match = 0; + // if we want to always create new tiles, don't bother trying to check matches + if (tileMatchThreshold < 64) { + _.each(tilesForMatch, function(tile) { + tile.match = 0; - _.each(tile.bitmap, function(row, y) { - _.each(row, function(pixel, x) { - if (parseInt(pixel) === parseInt(pseudoTile[y][x])) { - tile.match++; - } - }); - }); - - if (tile.secondAnimationFrame) { - _.each(tile.secondAnimationFrame, function(row, y) { + _.each(tile.bitmap, function(row, y) { _.each(row, function(pixel, x) { if (parseInt(pixel) === parseInt(pseudoTile[y][x])) { tile.match++; @@ -286,62 +301,100 @@ $(document).ready(function() { }); }); - tile.match /= 2; - } - }); + if (tile.secondAnimationFrame) { + _.each(tile.secondAnimationFrame, function(row, y) { + _.each(row, function(pixel, x) { + if (parseInt(pixel) === parseInt(pseudoTile[y][x])) { + tile.match++; + } + }); + }); - var bestMatch = _.first(_.sortBy(tilesForMatch, 'match')); + tile.match /= 2; + } + }); + } - // if best match is under threshold + // what if there are several equally good matches? + // find highest match amount and find all of them + var bestMatchAmount = _.last(_.sortBy(tilesForMatch, ['match'])).match; + var bestMatches = _.filter(tilesForMatch, {'match': bestMatchAmount}); + + // sort by name in ascending order + // earlier names are preferable + var bestMatch = _.first(_.sortBy(bestMatches, 'name')); + + if (tileMatchThreshold === 64 || bestMatch.match <= tileMatchThreshold) { // turn pseudo-tile into a real tile and add it to the tile data - - room.push(bestMatch.name); + + var name = newTileName(); + + bitsyData.tiles.push({ + name: name, + bitmap: pseudoTile, + new: true + }); + + room.push(name); + + // issue with this approach: + // what if a tile we add late in the loop is a better match for an earlier "good enough" match? + // this would also cause different results if the user were to add the same room several times + // we could keep iterating until the room no longer changes + } else { + room.push(bestMatch.name); + } }); }); room = _.chunk(room, 16); // write room to output + imageData = document.getElementById("room-output").getContext('2d').getImageData(0, 0, 128, 128); rawData = imageData.data; _.each(room, function(row, tileY) { _.each(row, function(tileName, tileX) { - if (_.get(bitsyData, 'tiles.' + tileName + '.bitmap')) { - _.each(bitsyData.tiles[tileName].bitmap, function(row, y) { - _.each(row, function(pixel, x) { - var position = (((tileY * 8) + y) * 128) + ((tileX * 8) + x); + var tile = _.find(bitsyData.tiles, {'name' : tileName}); - position *= 4; // 4 values (rgba) per pixel + _.each(tile.bitmap, function(row, y) { + _.each(row, function(pixel, x) { + var position = (((tileY * 8) + y) * 128) + ((tileX * 8) + x); - if (parseInt(pixel) === 0) { - var pixelColour = palette.background; - } else { - var pixelColour = palette.tile; - } + position *= 4; // 4 values (rgba) per pixel - rawData[position ] = pixelColour.red; - rawData[position + 1] = pixelColour.green; - rawData[position + 2] = pixelColour.blue; - rawData[position + 3] = 255; - }); + var pixelColour = {}; + + switch(parseInt(pixel)) { + case 0: pixelColour = palette.background; break; + case 1: pixelColour = palette.tile; break; + default: console.log("error"); + } + + rawData[position ] = pixelColour.red; + rawData[position + 1] = pixelColour.green; + rawData[position + 2] = pixelColour.blue; + rawData[position + 3] = 255; }); - } + }); }); }); document.getElementById('room-output').getContext('2d').putImageData(imageData, 0, 0); }); - }, 30); + } - $croppie.on('update', renderResult); + var renderDebounced = _.debounce(render, 30); - // make this not debounced but called every n milliseconds - $('#brightness').on('change', renderResult); + $croppie.on('update', renderDebounced); + + $('#brightness').on('change', renderDebounced); $('#brightness').on('dblclick', function() { $(this).val(0); - renderResult(); + + renderDebounced(); }); $('label[for="brightness"]').on('click touchdown', function() { @@ -375,7 +428,20 @@ $(document).ready(function() { // sprite colour is not currently used } - renderResult(); + renderDebounced(); + }); + + $(document).on('change', '#threshold', function() { + var newValue = parseInt($(this).val()); + + if (newValue < tileMatchThreshold) { + // set tiles back to default + bitsyData.tiles = _.filter(bitsyData.tiles, ['new', false]); + } + + tileMatchThreshold = newValue; + + renderDebounced(); }); $('#save').on('click touchend', function() { diff --git a/includes/style.css b/includes/style.css index 327cd4f..e80ec04 100644 --- a/includes/style.css +++ b/includes/style.css @@ -74,10 +74,12 @@ input { background-color: #d3cbd0; color: #594a54; } -#brightness, -#threshold { +#brightness { width: 256px; } +#threshold { + width: 150px; +} #brightness + label, #threshold + label { margin: 0 auto; diff --git a/includes/style.less b/includes/style.less index c4dcc32..4ba3cf8 100644 --- a/includes/style.less +++ b/includes/style.less @@ -95,13 +95,20 @@ textarea, input { color: @dark; } +#brightness { + width: 256px; +} + +#threshold { + width: 150px; +} + #brightness, #threshold { + label{ .centre; } // todo make this match the croppie slider or vice versa - width: 256px; } #imageUpload { diff --git a/index.html b/index.html index 5fae3f4..a9f25f6 100644 --- a/index.html +++ b/index.html @@ -5229,4 +5229,4 @@ DLG ITM_0 You found a nice warm cup of tea VAR a -42