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 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= 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