Skip to content

Instantly share code, notes, and snippets.

@gouldingken
Last active August 6, 2020 06:27
square pack to fill any SVG shape
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Shape Circles</title>
<script src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
</head>
<body>
<script src="squarePackShape.js"></script>
</body>
</html>
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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'));
});
});
@s4l4x
Copy link

s4l4x commented Aug 6, 2020

Instead of drawing into a context, "draw" into an with s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment