hippie/source/view/demo/art/squares.liquid

126 lines
No EOL
3.4 KiB
Text

---
title: Squares²
tags:
- demoArt
---
{% layout 'hippie/simple.liquid' %}
{% block style %}
<style>
canvas {
display: block;
}
</style>
{% endblock %}
{% block body %}{% endblock %}
{% block script %}
<script>
function fillCanvasWithSquares(maxSquareSize = 128, minSquareSize = 8, largeSquarePercentage = 40) {
const canvas = document.createElement('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
const colors = [
'#fad803', '#d30a51', '#273f8b',
'#b7e0f0', '#52bed1', '#0c85ff'
];
function isPowerOfTwo(n) {
return n > 0 && (n & (n - 1)) === 0;
}
if (!isPowerOfTwo(maxSquareSize) || !isPowerOfTwo(minSquareSize)) {
console.error('Both sizes must be powers of 2');
return;
}
let grid = Array(Math.ceil(canvas.height / minSquareSize))
.fill(null)
.map(() => Array(Math.ceil(canvas.width / minSquareSize)).fill(false));
function canPlace(gridX, gridY, sizeInCells) {
if (gridY + sizeInCells > grid.length || gridX + sizeInCells > grid[0].length) return false;
for (let y = gridY; y < gridY + sizeInCells; y++) {
for (let x = gridX; x < gridX + sizeInCells; x++) {
if (grid[y][x]) return false;
}
}
return true;
}
function markOccupied(gridX, gridY, sizeInCells) {
for (let y = gridY; y < gridY + sizeInCells; y++) {
for (let x = gridX; x < gridX + sizeInCells; x++) {
grid[y][x] = true;
}
}
}
function draw(gridX, gridY, sizeInPixels) {
const x = gridX * minSquareSize;
const y = gridY * minSquareSize;
ctx.fillStyle = colors[Math.floor(Math.random() * colors.length)];
ctx.fillRect(x, y, sizeInPixels, sizeInPixels);
ctx.strokeStyle = '#333';
ctx.lineWidth = 1;
ctx.strokeRect(x, y, sizeInPixels, sizeInPixels);
}
function fill() {
const largestSizeInCells = maxSquareSize / minSquareSize;
// Collect and shuffle positions for largest squares
const positions = [];
for (let gridY = 0; gridY < grid.length; gridY++) {
for (let gridX = 0; gridX < grid[0].length; gridX++) {
if (canPlace(gridX, gridY, largestSizeInCells)) {
positions.push({ gridX, gridY });
}
}
}
positions.sort(() => Math.random() - 0.5);
// Place largest squares up to percentage
const target = Math.floor(positions.length * largeSquarePercentage / 100);
for (let i = 0; i < target; i++) {
const { gridX, gridY } = positions[i];
if (canPlace(gridX, gridY, largestSizeInCells)) {
markOccupied(gridX, gridY, largestSizeInCells);
draw(gridX, gridY, maxSquareSize);
}
}
// Fill remaining space with smaller sizes
for (let size = maxSquareSize / 2; size >= minSquareSize; size /= 2) {
const cellSize = size / minSquareSize;
for (let gridY = 0; gridY < grid.length; gridY++) {
for (let gridX = 0; gridX < grid[0].length; gridX++) {
if (canPlace(gridX, gridY, cellSize)) {
markOccupied(gridX, gridY, cellSize);
draw(gridX, gridY, size);
}
}
}
}
}
fill();
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
grid = Array(Math.ceil(canvas.height / minSquareSize))
.fill(null)
.map(() => Array(Math.ceil(canvas.width / minSquareSize)).fill(false));
ctx.clearRect(0, 0, canvas.width, canvas.height);
fill();
});
}
fillCanvasWithSquares(128, 8, 10);
</script>
{% endblock %}