2026-04-20 20:13:58 +02:00
|
|
|
---
|
|
|
|
|
title: Squares²
|
|
|
|
|
tags:
|
|
|
|
|
- demoArt
|
|
|
|
|
---
|
2026-04-23 21:45:14 +02:00
|
|
|
{% layout 'hippie-view/simple.liquid' %}
|
2026-04-20 20:13:58 +02:00
|
|
|
|
|
|
|
|
{% block style %}
|
|
|
|
|
<style>
|
|
|
|
|
canvas {
|
|
|
|
|
display: block;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
{% endblock %}
|
|
|
|
|
|
|
|
|
|
{% block body %}{% endblock %}
|
|
|
|
|
|
|
|
|
|
{% block script %}
|
|
|
|
|
<script>
|
2026-04-20 21:05:35 +02:00
|
|
|
function fillCanvasWithSquares(maxSquareSize = 128, steps = 3, percentages = [40, 30, 20]) {
|
2026-04-20 20:13:58 +02:00
|
|
|
const canvas = document.createElement('canvas');
|
|
|
|
|
canvas.width = window.innerWidth;
|
|
|
|
|
canvas.height = window.innerHeight;
|
|
|
|
|
document.body.appendChild(canvas);
|
|
|
|
|
|
|
|
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
|
const colors = [
|
2026-04-20 21:05:35 +02:00
|
|
|
'#e1d170',
|
|
|
|
|
'#cfb16a',
|
|
|
|
|
'#8b3050',
|
|
|
|
|
'#683657',
|
|
|
|
|
'#354063',
|
|
|
|
|
'#4b5776',
|
|
|
|
|
'#c8dbe2',
|
|
|
|
|
'#90bac2',
|
|
|
|
|
'#7daeb7'
|
2026-04-20 20:13:58 +02:00
|
|
|
];
|
2026-04-20 22:59:29 +02:00
|
|
|
/*const colors = [
|
|
|
|
|
'#fad803',
|
|
|
|
|
'#f2af13',
|
|
|
|
|
'#d30a51',
|
|
|
|
|
'#8e1f68',
|
|
|
|
|
'#273f8b',
|
|
|
|
|
'#3c579a',
|
|
|
|
|
'#b7e0f0',
|
|
|
|
|
'#6bc7d9',
|
|
|
|
|
'#52bed1'
|
|
|
|
|
];*/
|
2026-04-20 20:13:58 +02:00
|
|
|
|
|
|
|
|
function isPowerOfTwo(n) {
|
|
|
|
|
return n > 0 && (n & (n - 1)) === 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-20 21:05:35 +02:00
|
|
|
if (!isPowerOfTwo(maxSquareSize)) {
|
|
|
|
|
console.error('maxSquareSize must be a power of 2');
|
2026-04-20 20:39:35 +02:00
|
|
|
return;
|
2026-04-20 20:13:58 +02:00
|
|
|
}
|
|
|
|
|
|
2026-04-20 21:05:35 +02:00
|
|
|
if (steps < 1) {
|
|
|
|
|
console.error('steps must be at least 1');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generate sizes by halving for each step
|
|
|
|
|
const sizes = [];
|
|
|
|
|
for (let i = 0; i < steps; i++) {
|
|
|
|
|
sizes.push(maxSquareSize / Math.pow(2, i));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate percentages array
|
|
|
|
|
if (!Array.isArray(percentages)) {
|
|
|
|
|
console.error('percentages must be an array');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (percentages.length !== sizes.length - 1) {
|
|
|
|
|
console.warn(`percentages array should have ${sizes.length - 1} elements. Adjusting...`);
|
|
|
|
|
percentages = percentages.slice(0, sizes.length - 1);
|
|
|
|
|
while (percentages.length < sizes.length - 1) {
|
|
|
|
|
percentages.push(50);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const minSize = sizes[sizes.length - 1];
|
|
|
|
|
|
|
|
|
|
let grid = Array(Math.ceil(canvas.height / minSize))
|
2026-04-20 20:39:35 +02:00
|
|
|
.fill(null)
|
2026-04-20 21:05:35 +02:00
|
|
|
.map(() => Array(Math.ceil(canvas.width / minSize)).fill(false));
|
2026-04-20 20:13:58 +02:00
|
|
|
|
2026-04-20 20:39:35 +02:00
|
|
|
function canPlace(gridX, gridY, sizeInCells) {
|
|
|
|
|
if (gridY + sizeInCells > grid.length || gridX + sizeInCells > grid[0].length) return false;
|
2026-04-20 20:13:58 +02:00
|
|
|
for (let y = gridY; y < gridY + sizeInCells; y++) {
|
|
|
|
|
for (let x = gridX; x < gridX + sizeInCells; x++) {
|
|
|
|
|
if (grid[y][x]) return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-20 20:39:35 +02:00
|
|
|
function markOccupied(gridX, gridY, sizeInCells) {
|
2026-04-20 20:13:58 +02:00
|
|
|
for (let y = gridY; y < gridY + sizeInCells; y++) {
|
|
|
|
|
for (let x = gridX; x < gridX + sizeInCells; x++) {
|
|
|
|
|
grid[y][x] = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-20 20:39:35 +02:00
|
|
|
function draw(gridX, gridY, sizeInPixels) {
|
2026-04-20 21:05:35 +02:00
|
|
|
const x = gridX * minSize;
|
|
|
|
|
const y = gridY * minSize;
|
2026-04-20 22:59:29 +02:00
|
|
|
const type = Math.random() < 0.5;
|
2026-04-20 21:05:35 +02:00
|
|
|
|
2026-04-20 20:39:35 +02:00
|
|
|
ctx.fillStyle = colors[Math.floor(Math.random() * colors.length)];
|
|
|
|
|
ctx.fillRect(x, y, sizeInPixels, sizeInPixels);
|
2026-04-20 22:59:29 +02:00
|
|
|
// TODO: Linie anpassen und wiederholen
|
|
|
|
|
ctx.strokeStyle = '#404040';
|
|
|
|
|
// ctx.strokeStyle = type ? '#fff' : '#000';
|
2026-04-20 20:13:58 +02:00
|
|
|
ctx.lineWidth = 1;
|
2026-04-20 20:39:35 +02:00
|
|
|
ctx.strokeRect(x, y, sizeInPixels, sizeInPixels);
|
2026-04-20 20:13:58 +02:00
|
|
|
}
|
|
|
|
|
|
2026-04-20 20:39:35 +02:00
|
|
|
function fill() {
|
2026-04-20 21:05:35 +02:00
|
|
|
// Process each size with its corresponding percentage
|
|
|
|
|
for (let sizeIndex = 0; sizeIndex < sizes.length; sizeIndex++) {
|
|
|
|
|
const size = sizes[sizeIndex];
|
|
|
|
|
const sizeInCells = size / minSize;
|
|
|
|
|
const percentage = sizeIndex < percentages.length ? percentages[sizeIndex] : 0;
|
|
|
|
|
|
|
|
|
|
if (sizeIndex < sizes.length - 1) {
|
|
|
|
|
// For all sizes except the last, apply percentage
|
|
|
|
|
const positions = [];
|
|
|
|
|
for (let gridY = 0; gridY < grid.length; gridY++) {
|
|
|
|
|
for (let gridX = 0; gridX < grid[0].length; gridX++) {
|
|
|
|
|
if (canPlace(gridX, gridY, sizeInCells)) {
|
|
|
|
|
positions.push({ gridX, gridY });
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-20 20:13:58 +02:00
|
|
|
}
|
2026-04-20 21:05:35 +02:00
|
|
|
positions.sort(() => Math.random() - 0.5);
|
2026-04-20 20:13:58 +02:00
|
|
|
|
2026-04-20 21:05:35 +02:00
|
|
|
const target = Math.floor(positions.length * percentage / 100);
|
|
|
|
|
for (let i = 0; i < target; i++) {
|
|
|
|
|
const { gridX, gridY } = positions[i];
|
|
|
|
|
if (canPlace(gridX, gridY, sizeInCells)) {
|
|
|
|
|
markOccupied(gridX, gridY, sizeInCells);
|
2026-04-20 20:39:35 +02:00
|
|
|
draw(gridX, gridY, size);
|
2026-04-20 20:13:58 +02:00
|
|
|
}
|
|
|
|
|
}
|
2026-04-20 21:05:35 +02:00
|
|
|
} else {
|
|
|
|
|
// Fill remaining space with the smallest size
|
|
|
|
|
for (let gridY = 0; gridY < grid.length; gridY++) {
|
|
|
|
|
for (let gridX = 0; gridX < grid[0].length; gridX++) {
|
|
|
|
|
if (canPlace(gridX, gridY, sizeInCells)) {
|
|
|
|
|
markOccupied(gridX, gridY, sizeInCells);
|
|
|
|
|
draw(gridX, gridY, size);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-20 20:13:58 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-20 20:39:35 +02:00
|
|
|
fill();
|
2026-04-20 20:13:58 +02:00
|
|
|
|
|
|
|
|
window.addEventListener('resize', () => {
|
|
|
|
|
canvas.width = window.innerWidth;
|
|
|
|
|
canvas.height = window.innerHeight;
|
2026-04-20 21:05:35 +02:00
|
|
|
grid = Array(Math.ceil(canvas.height / minSize))
|
2026-04-20 20:39:35 +02:00
|
|
|
.fill(null)
|
2026-04-20 21:05:35 +02:00
|
|
|
.map(() => Array(Math.ceil(canvas.width / minSize)).fill(false));
|
2026-04-20 20:13:58 +02:00
|
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
2026-04-20 20:39:35 +02:00
|
|
|
fill();
|
2026-04-20 20:13:58 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-20 21:05:35 +02:00
|
|
|
fillCanvasWithSquares(128, 4, [2, 24, 64]);
|
2026-04-20 20:13:58 +02:00
|
|
|
</script>
|
|
|
|
|
{% endblock %}
|