<svg viewBox="0 0 1170 1650" width="1170" height="1650" xmlns="http://www.w3.org/2000/svg" version="1.1" >
<!--
Johan Karlsson, 2020
https://twitter.com/DonKarlssonSan
MIT License, see Details View
A3 paper size: 1650 x 1170
-->
</svg>
html, body {
height: 100%;
margin: 0;
cursor: pointer;
background-color: white;
}
svg {
position: absolute;
width: 100%;
height: 100%;
}
/*
Johan Karlsson, 2020
https://twitter.com/DonKarlssonSan
MIT License, see Details View
*/
const svgNs = "http://www.w3.org/2000/svg";
const w = 1170;
const h = 1650;
let svg;
let circles;
let imageBuffer;
class Circle {
constructor(x, y) {
this.x = x;
this.y = y;
this.r = 8;
this.done = false;
}
draw(groupElement) {
let circle = document.createElementNS(svgNs, "circle");
circle.setAttribute("cx", this.x);
circle.setAttribute("cy", this.y);
circle.setAttribute("r", this.r);
groupElement.appendChild(circle);
}
}
function setup() {
svg = document.querySelector("svg");
document.addEventListener("click", draw);
document.addEventListener("keydown", onKeyDown);
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
canvas.width = w;
canvas.height = h;
storeTextInBuffer(ctx);
}
function storeTextInBuffer(ctx) {
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.font = `bold 440px sans-serif`;
ctx.fillText("LEXI", w / 2, h * 0.2);
ctx.font = `bold 1200px sans-serif`;
ctx.fillText("5", w / 2, h * 0.7);
let image = ctx.getImageData(0, 0, w, h);
imageBuffer = new Uint32Array(image.data.buffer);
}
function onKeyDown (e) {
if(e.code === "KeyD") {
download();
}
}
function download() {
let svgDoc = svg.outerHTML;
let filename = "packed-circles.svg";
let element = document.createElement("a");
element.setAttribute("href", "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svgDoc));
element.setAttribute("download", filename);
element.style.display = "none";
document.body.appendChild(element);
element.addEventListener("click", e => e.stopPropagation());
element.click();
document.body.removeChild(element);
}
function dist(x1, y1, x2, y2) {
return Math.hypot(x1 - x2, y1 - y2);
}
function addCircles() {
let nrOfTries = 0;
let wasAdded;
do {
wasAdded = false;
let x = Math.random() * w;
let y = Math.random() * h;
if(validPos(x, y)) {
wasAdded = true;
let c = new Circle(x, y);
circles.push(c);
}
nrOfTries++;
} while (!wasAdded && nrOfTries < 10)
}
function validPos(x, y) {
let index = Math.round(y) * w + Math.round(x);
let isInside = index < imageBuffer.length && imageBuffer[index];
if(isInside) {
return false;
}
for(let i = 0; i < circles.length; i++) {
let current = circles[i];
let d = dist(x, y, current.x, current.y);
if(d - 8 < current.r) {
return false;
}
}
return true;
}
function canGrow(circle) {
for(let i = 0; i < circles.length; i++) {
let current = circles[i];
if(circle !== current) {
let d = dist(circle.x, circle.y, current.x, current.y);
if(d - 4 <= circle.r + current.r) {
return false;
}
}
}
return true;
}
function resetCircles() {
circles = [];
let nrOfTries = w * h / 1800;
for(let i = 0; i < nrOfTries; i++) {
addCircles();
}
}
function packCircles() {
let nrOfTries = w * h / 400;
for(let i = 0; i < nrOfTries; i++) {
addCircles();
circles.filter(c => !c.done).forEach(c => {
if(canGrow(c)) {
c.r += 1;
} else {
c.done = true;
}
});
}
}
function drawCircles() {
let group = document.querySelector("#container");
if(group) {
group.remove();
}
group = document.createElementNS(svgNs, "g");
group.setAttribute("id", "container");
group.setAttribute("fill", "none");
group.setAttribute("stroke", "black");
circles.forEach(c => c.draw(group));
svg.appendChild(group);
}
function draw() {
console.clear();
resetCircles();
packCircles();
drawCircles();
}
setup();
draw();
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.