|
var _canvasProps = {width: 300, height: 300}; |
|
var _options = {spacing: 1, numSquares: 1000, minSize: 2, maxSize: 20, higherAccuracy: false}; |
|
var _placedSquaresArr = []; |
|
|
|
var _isFilled = function (imgData, imageWidth, x, y) { |
|
x = Math.round(x); |
|
y = Math.round(y); |
|
var a = imgData.data[((imageWidth * y) + x) * 4 + 3]; |
|
return a > 0; |
|
}; |
|
|
|
var _isSquareInside = function (imgData, imageWidth, x, y, size) { |
|
//if (!_isFilled(imgData, imageWidth, x, y)) return false; |
|
//--use 4 points around square as good enough approximation |
|
if (!_isFilled(imgData, imageWidth, x - size / 2, y + size / 2)) return false; |
|
if (!_isFilled(imgData, imageWidth, x + size / 2, y + size / 2)) return false; |
|
if (!_isFilled(imgData, imageWidth, x - size / 2, y - size / 2)) return false; |
|
if (!_isFilled(imgData, imageWidth, x + size / 2, y - size / 2)) return false; |
|
if (_options.higherAccuracy) { |
|
//--use another 4 points between the others as better approximation |
|
if (!_isFilled(imgData, imageWidth, x, y + size / 2)) return false; |
|
if (!_isFilled(imgData, imageWidth, x, y - size / 2)) return false; |
|
if (!_isFilled(imgData, imageWidth, x - size / 2, y)) return false; |
|
if (!_isFilled(imgData, imageWidth, x + size / 2, y)) return false; |
|
} |
|
return true; |
|
}; |
|
|
|
var _touchesPlacedSquare = function (x, y, size) { |
|
var r1 = size / 2 + _options.spacing / 2; |
|
return _placedSquaresArr.some(function (square) { |
|
var r2 = square.size / 2 + _options.spacing / 2; |
|
return (x + r1 > square.x - r2) && |
|
(x - r1 < square.x + r2) && |
|
(y + r1 > square.y - r2) && |
|
(y - r1 < square.y + r2); |
|
}); |
|
}; |
|
|
|
var _dist = function (x1, y1, x2, y2) { |
|
var a = x1 - x2; |
|
var b = y1 - y2; |
|
return Math.sqrt(a * a + b * b); |
|
}; |
|
|
|
var _placeSquares = function (imgData) { |
|
var i = _squares.length; |
|
_placedSquaresArr = []; |
|
while (i > 0) { |
|
i--; |
|
var square = _squares[i]; |
|
var safety = 1000; |
|
while (!square.x && safety-- > 0) { |
|
var x = Math.random() * _canvasProps.width; |
|
var y = Math.random() * _canvasProps.height; |
|
if (_isSquareInside(imgData, _canvasProps.width, x, y, square.size)) { |
|
if (!_touchesPlacedSquare(x, y, square.size)) { |
|
square.x = x; |
|
square.y = y; |
|
_placedSquaresArr.push(square); |
|
} |
|
} |
|
} |
|
} |
|
}; |
|
|
|
var _makeSquares = function () { |
|
var squares = []; |
|
for (var i = 0; i < _options.numSquares; i++) { |
|
var square = { |
|
color: _colors[Math.round(Math.random() * _colors.length)], |
|
size: _options.minSize + Math.random() * Math.random() * (_options.maxSize - _options.minSize) //do random twice to prefer more smaller ones |
|
}; |
|
squares.push(square); |
|
} |
|
squares.sort(function (a, b) { |
|
return a.size - b.size; |
|
}); |
|
return squares; |
|
}; |
|
|
|
var _drawSquares = function (ctx) { |
|
ctx.save(); |
|
$.each(_squares, function (i, square) { |
|
ctx.fillStyle = square.color; |
|
ctx.beginPath(); |
|
ctx.rect(square.x - square.size / 2, square.y - square.size / 2, square.size, square.size); |
|
ctx.fill() |
|
}); |
|
|
|
ctx.restore(); |
|
}; |
|
|
|
var _drawSvg = function (ctx, path, callback) { |
|
var img = new Image(ctx); |
|
img.onload = function () { |
|
ctx.drawImage(img, 0, 0); |
|
callback(); |
|
}; |
|
img.src = path; |
|
}; |
|
|
|
var _colors = ['#993300', '#a5c916', '#00AA66', '#FF9900']; |
|
var _squares = _makeSquares(); |
|
|
|
$(document).ready(function () { |
|
var $canvas = $('<canvas>').attr(_canvasProps).appendTo('body'); |
|
var $canvas2 = $('<canvas>').attr(_canvasProps).appendTo('body'); |
|
var ctx = $canvas[0].getContext('2d'); |
|
_drawSvg(ctx, 'note.svg', function () { |
|
var imgData = ctx.getImageData(0, 0, _canvasProps.width, _canvasProps.height); |
|
_placeSquares(imgData); |
|
_drawSquares($canvas2[0].getContext('2d')); |
|
}); |
|
|
|
}); |
This is a simple modification of http://bl.ocks.org/gouldingken/8d0b7a05b0b0156da3b8#note.svg that uses squares instead of circles.