/**
* Finds individual shapes within a map
* This map can be a bitmap if code is added to get canvas img data
* This is just a proof of concept for the [Find the length of a coastline] project
*
* http://jeremyheminger.com/article/programming/find-length-coastline-hypothesis
* http://jeremyheminger.com/find-length-coastline-programming-part-1
* http://jeremyheminger.com/find-length-coastline-programming-part-2
* http://jeremyheminger.com/find-length-coastline-programming-part-3
*
* To view the resulting array Right-Click and Inspect
* */
//@param {Number} reference the canvas
var i;
//@param {Array}
var LOOP = [];
//@param {Array}
var RESULT = [];
//@param {Number}
var MAXLOOPS = 200;
//@param {Number}
var _I = 0;
//@param {Number}
var _L = 0;
//@param {Number}
var yl = xy.length;
//@param {Number}
var xl = xy[0].length;
//@param {Array} this is the map we will be testing
var xy = [
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,1,1,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0],
[0,0,0,0,1,1,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0],
[0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,0,1,1,1,1,1,1,0,0,0],
[0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1,1,1,0],
[0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,1,1,1,1,0,0,0],
[0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,1,1,1,1,0,0,0],
[0,0,0,0,0,1,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,0,0,0],
[0,0,0,0,0,0,0,1,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,1,0,0,1,1,1,1,1,1,0,1,0],
[0,0,0,0,1,1,1,1,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,1,0,0,1,1,1,1,1,1,0,1,0],
[0,0,0,0,1,1,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,1,0],
[0,0,0,0,1,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,1,1,1,1,1,1,0,1,0],
[0,0,0,0,0,1,1,1,1,0,1,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,1,1,1,1,1,1,0,1,0],
[0,0,0,0,0,0,1,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0],
[0,0,0,0,0,0,1,1,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0],
[0,0,0,0,0,1,1,0,0,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0],
[0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,1,1,0,0,0],
[0,0,0,0,0,1,0,0,0,0,0,1,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0],
[0,0,0,0,0,1,1,1,1,0,0,0,0,1,0,0,0,0,1,0,1,1,1,0,0,0,0,0,0,0,0,1,0,0,0],
[0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,0,0,0,0,0,0,1,0,0,0],
[0,0,0,0,0,0,1,1,1,1,1,1,0,0,1,1,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0],
[0,1,1,0,0,0,0,1,1,1,1,1,0,0,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0],
[0,0,1,0,0,0,0,1,1,1,1,1,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0],
[0,0,1,1,1,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0],
[0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0],
[0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0],
[0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0],
[0,0,0,0,1,1,1,0,0,1,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0],
[0,0,0,0,1,1,1,1,1,1,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0],
[0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0],
[0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0],
[0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0],
[0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
];
// when the browser is ready
window.onload = function() {
'use strict';
// initialize a Wes Mantooth canvas
i = $w.canvas.init(document.getElementById('target'));
// draw the grid
$w.draw.grid(i,1000,1000,10);
run();
}
/**
* run everything when ready
* @returns {Void}
**/
function run() {
// loop the map
for(var y=0; y<yl; y++) {
for(var x=0; x<xl; x++) {
// if this pixel is a target and has not yet been tested
if (xy[y][x] == 1) {
// draw a dot here
$w.canvas.rectangle(i,x*10,y*10,10,10,'#2c07b9','fill');
// add the x,y coordinate to an array
LOOP.push([x,y]);
}
}
}
// get the length of the loop array
_L = LOOP.length;
// run the loop with a promise in order to allow asynchronous
let promise = look(xy,LOOP[_I][0],LOOP[_I][1]);
promise.then(successCallback, failureCallback);
}
/**
* @returns {Void}
* */
function successCallback() {
_I++;
if (_I<_L) {
setTimeout(function(){
console.log('RUNNING');
let promise = look(xy,LOOP[_I][0],LOOP[_I][1]);
promise.then(successCallback, failureCallback);
},100);
}else{
console.log(RESULT);
}
}
/**
* @returns {Void}
* */
function failureCallback(error) {
console.log('failureCallback');
console.log(error);
}
/**
* @param {Array}
* @param {Number}
* #param {Number}
* @returns {Void}
* */
async function look(xy,x,y) {
// check if this shape has already been found
if (xy[y][x] != 1) {
console.log('NO NEED TO RUN');
return;
}
// @param {Array} a local array to test against
var RESULTnow = [];
console.log('look');
//@param {Object}
var obj = {
x:x,
y:y,
xy:xy,
m:true,
i:0
}
// run a test on the first pixel
obj = move(obj);
//@param {Boolean}
var moves = true;
//@param {Number}
var j = 0;
//@param {Number}
var id = setInterval(function() {
if(obj.i == 2)
RESULTnow.push([obj.x,obj.y]);
// run the test
obj = move(obj);
// update if there are any moves left
moves = obj.m;
// increment the counter
j++;
// if the test has run too many times its likely never to complete
if (j > MAXLOOPS) {
console.log('MAX LOOPS');
moves = false;
j = 0;
}
if (!moves) {
clearInterval(id);
console.log("NO MORE MOVES");
var duplicate = false;
var rl = RESULT.length;
var rnl = RESULTnow.length;
for(var j=0; j<rl; j++) {
for(var k = 0; k<rnl;k++) {
if (RESULTnow[k][0] == RESULT[j][0] && RESULTnow[j][1] == RESULT[k][1]) {
duplicate = true;
}
}
}
if (!duplicate) {
RESULT.push(RESULTnow);
}
}
},1);
}
function move(obj) {
//@param {Number}
let max = 15;
//@param {Boolean}
let moved = false;
// increment the current map locations weight
obj.xy[obj.y][obj.x]++;
obj.i = obj.xy[obj.y][obj.x];
//@param {Number}
let c = obj.xy[obj.y][obj.x] * 10;
//@param {String}
let color = $w.color.rgbToHex(c*2,c*3,200);
// update the dot color on the map
$w.canvas.rectangle(0,(obj.x)*10,(obj.y)*10,10,10,color,'fill');
//@param {Array}
let dirs = [null,null,null,null];
// if the value of a key is set get its current weight
if (undefined !== obj.xy[obj.y-1] && obj.xy[obj.y-1][obj.x] > 0)dirs[0] = obj.xy[obj.y-1][obj.x];
if (undefined !== obj.xy[obj.y][obj.x+1] && obj.xy[obj.y][obj.x+1] > 0)dirs[1] = obj.xy[obj.y][obj.x+1];
if (undefined !== obj.xy[obj.y+1] && obj.xy[obj.y+1][obj.x] > 0)dirs[2] = obj.xy[obj.y+1][obj.x];
if (undefined !== obj.xy[obj.y][obj.x-1] && obj.xy[obj.y][obj.x-1] > 0)dirs[3] = obj.xy[obj.y][obj.x-1];
// loop the directions to get the lowest weight
var min = max, dir = 0;
for(let i = 0; i<4; i++) {
if (dirs[i] != null)
if (dirs[i] < min){
min = dirs[i];
dir = i;
}
}
// make sure that the weight is less than the max
if (min < max) {
// move the test based on the direction with the lowest weight
switch(dir) {
case 0:
obj.y--;
moved = true;
break;
case 1:
obj.x++;
moved = true;
break;
case 2:
obj.y++;
moved = true;
break;
case 3:
obj.x--;
moved = true;
break;
}
}
// if a move was made then pass that result back
if (!moved)obj.m = false;
return obj;
}