Skip to content

Instantly share code, notes, and snippets.

@gouldingken
Last active August 6, 2020 06:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gouldingken/25df41d631a3557d80b7 to your computer and use it in GitHub Desktop.
Save gouldingken/25df41d631a3557d80b7 to your computer and use it in GitHub Desktop.
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>
Display the source blob
Display the rendered blob
Raw
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'));
});
});
@gouldingken
Copy link
Author

This is a simple modification of http://bl.ocks.org/gouldingken/8d0b7a05b0b0156da3b8#note.svg that uses squares instead of circles.

Copy link

ghost commented May 11, 2017

This is awesome! Do you have any pointer on how would this work within svg paths and no canvas at all? Thank you!

@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