hex-grid-wallpaper/script.js
Christian Lawson-Perfect 2111c52a64 first commit
2025-02-09 20:07:07 +00:00

256 lines
No EOL
6.7 KiB
JavaScript

function element(name, attr, content, children) {
const e = document.createElementNS('http://www.w3.org/2000/svg', name);
if(attr) {
Object.entries(attr).forEach(([k,v]) => e.setAttribute(k,v));
}
if(content !== undefined) {
e.innerHTML = content;
}
if(children) {
children.forEach(c => e.append(c));
}
return e;
}
function interpolator(fn) {
return function(strs, ...subs) {
let o = '';
strs.forEach((s,i) => {
o += s;
if(i<subs.length) {
o += fn(subs[i]);
}
});
return o;
}
}
const dp = interpolator(x => x.toFixed(4))
const coords = interpolator(([x,y]) => dp`${x} ${y}`);
function midpoint([x1,y1],[x2,y2]) {
return [(x1+x2)/2, (y1+y2)/2];
}
const {cos, sin, PI, sqrt} = Math;
const hex_points = [0,1,2,3,4,5].map(i => [cos(i*PI/3)/sqrt(3), sin(i*PI/3)/sqrt(3)]);
const midpoints = [0,1,2,3,4,5].map(i => midpoint(hex_points[i], hex_points[(i+1)%6]));
const svg = document.getElementById('board');
svg.append()
const dr = [cos(PI/6),sin(PI/6)];
const [drx,dry] = dr;
const dc = [cos(PI/6),-sin(PI/6)];
const [dcx,dcy] = dc;
function hex_project(r,c) {
return [r*drx + c*dcx, r*dry + c*dcy];
}
function lerp(a,b,t) {
return (1-t)*a + t*b;
}
function clamp(a,b,t) {
return Math.max(a,Math.min(b,t));
}
function rlerp(a,b) {
return lerp(a,b,Math.random());
}
function rint(n) {
return Math.floor(Math.random()*n);
}
function draw_hex([x,y], sqd, hue) {
const hex_d = coords`M ${hex_points[0]} `+(hex_points.slice(1).map(p=>coords`L ${p}`)).join(' ');
const s = clamp(0,1, 2*sqd**0.3)*rlerp(0.8,1);
return element('path',{d:hex_d,fill: dp`hsl(${hue},50%,${rlerp(90,95)}%)`, transform: dp`translate(${x} ${y}) scale(${s})`})
}
function fill_hex_with_tris([x,y], sqd, hue) {
const g = element('g');
const density = clamp(0,1, 1.5*sqd**0.3);
for(let i=0;i<6;i++) {
if(Math.random() < density) {
g.append(draw_tri([x,y],i, hue));
}
}
return g;
}
function draw_tri([x,y], o, hue) {
o = o || 0;
const tri_d = coords`M ${hex_points[o%6]} L ${hex_points[(o+1)%6]} L ${[0,0]}`;
return element('path',{d:tri_d,fill: dp`hsl(${hue},50%,${rlerp(90,95)}%)`, transform: dp`translate(${x} ${y})`})
}
function draw_circle([x,y], sqd, hue) {
const l = clamp(90,95,1000*sqrt(sqd));
const radius = clamp(0, 1, 3*sqd**0.3) * rlerp(0.8,1) * 0.5;
return element('circle', {cx: x, cy: y, r: radius, fill: `hsl(${hue},50%,${rlerp(0.95,1)*l}%)`})
}
function draw_branches([x,y], sqd, hue) {
const w = clamp(0, 0.3, 0.3 * sqrt(sqd));
let o = Math.random() < 0.5 ? 0 : 1;
const branch_d = coords`M 0 0 L ${midpoints[o]} M 0 0 L ${midpoints[o+2]} M 0 0 L ${midpoints[o+4]}`;
return element('path',{d: branch_d, 'stroke-width': w, stroke: dp`hsl(${hue},50%,${rlerp(85,95)}%)`, transform: dp`translate(${x} ${y})`})
}
function draw_hex_outline([x,y], sqd, hue) {
let o = rint(6);
let n = Math.floor(clamp(1,6,80*sqd**1));
const w = 1 - clamp(0,0.7,lerp(0.2,0.7,1*sqd**0.5));
let outer = coords`M ${hex_points[o]}`;
let inner = '';
for(let i=0;i<=n;i++) {
const [px,py] = hex_points[(o+i)%6];
outer += coords`L ${[px,py]}`;
inner = coords` L ${[w*px,w*py]}` + inner;
}
const hex_d = outer + inner;
return element('path',{d:hex_d, fill: dp`hsl(${hue},50%,${rlerp(90,95)}%)`, transform: dp`translate(${x} ${y})`})
}
function draw_arcs([x,y], sqd, hue) {
const g = element('g');
const density = clamp(0, 1, 1.5*sqd**0.3);
for(let i=0;i<6;i++) {
if(Math.random() < density) {
const r = 1/sqrt(3)/2/density;
const arc = element('path', {fill: dp`hsl(${hue},50%,${rlerp(90,95)}%)`, d: dp`M ${hex_points[i][0]} ${hex_points[i][1]} L ${midpoints[i][0]} ${midpoints[i][1]} A ${r} ${r} 0 0 1 ${midpoints[(i+5)%6][0]} ${midpoints[(i+5)%6][1]}`, transform: dp`translate(${x} ${y})`})
g.append(arc);
}
}
g.append(element('circle', {cx: x, cy: y, fill: dp`hsl(${hue},50%,${rlerp(90,95)}%)`, r: 1/sqrt(3)/2*density}));
return g;
}
function choice(l) {
const i = Math.floor(Math.random() * l.length);
return l[i];
}
const fns = [
draw_branches,
draw_hex,
draw_hex_outline,
fill_hex_with_tris,
//draw_circle,
draw_arcs,
];
const spots = [];
function hue_for(x,y) {
const rhue = 40;
const hue = ((x+y)/80 + 0.5) * 360 + rlerp(-rhue, rhue);
return hue;
}
for(let i=0;i<20;i++) {
const fn = choice(fns);
const an = rlerp(0,2*PI);
const r = sqrt(rlerp(0,1))*30;
const pos = [r*cos(an), r*sin(an)];
const [x,y] = pos;
const size = rlerp(1, 5);
const hue = hue_for(x,y);
spots.push({fn,pos, hue, size});
const c = element('circle',{cx: pos[0], cy: pos[1], r: 1, fill: dp`hsl(${hue},100%,50%)`});
//svg.append(c);
}
function sqdist([x1,y1], [x2,y2]) {
return (x1-x2)**2 + (y1-y2)**2;
}
function weighted_choice(weights,things) {
let t = 0;
weights.forEach(x => t += x);
const x = Math.random() * t;
let z = 0;
for(let i=0;i<weights.length;i++) {
z += weights[i];
if(z >= x) {
return things[i];
}
}
}
function layer_hexes() {
for(let r=-40;r<40;r++) {
for(let c=-40;c<40;c++) {
const p = hex_project(r,c);
const weights = spots.map(({pos}) => {
return 1/sqdist(p, pos);
})
const {fn, pos, hue, size} = weighted_choice(weights, spots)
const el = fn(p, size/(1+sqdist(p,pos)), hue);
svg.append(el);
}
}
}
function layer_lines() {
const occupied = {};
function hash(x,y) {
return `${x},${y}`;
}
function is_occupied(x,y) {
return hash(x,y) in occupied;
}
for(let r=-30;r<30;r++) {
for(let c=-30;c<30;c+=1) {
if(is_occupied(r,c)) {
continue;
}
let [x,y] = [r,c];
const hue = hue_for(...hex_project(r,c));
let trace = '';
let steps = 10;
for(let i=0;i<1000;i++) {
const directions = [
[1,0],
[0,1],
[-1,0],
[0,-1],
[1,-1],
[-1,1]
];
const available = directions.filter(([dx,dy]) => !is_occupied(x+dx,y+dy));
console.log(available);
if(!available.length) {
break;
}
const [dx,dy] = choice(available);
console.log(dx,dy);
for(let n=0;n<steps && !is_occupied(x+dx,y+dy);n++) {
x = x+dx;
y = y+dy;
occupied[hash(x,y)] = true;
trace += (trace ? ' L' : 'M') + coords` ${hex_project(x,y)}`;
}
}
console.log(trace);
svg.append(element('path',{d:trace,fill:'none',stroke: `hsl(${hue},50%, ${rlerp(95,99)}%)`, 'stroke-width': rlerp(0.1,0.2), 'stroke-linejoin': 'round'}))
}
}
}
function layer_parquet() {
const h = cos(PI/3);
}
layer_lines();
layer_hexes();
const viewport = 15;
svg.setAttribute('viewBox', `${-viewport} ${-viewport} ${2*viewport} ${2*viewport}`)