diff --git a/_screenshots/2021-12-21_home.png b/_screenshots/2021-12-21_home.png new file mode 100644 index 0000000..d744606 Binary files /dev/null and b/_screenshots/2021-12-21_home.png differ diff --git a/assets/home.js b/assets/home.js new file mode 100644 index 0000000..fc4afa9 --- /dev/null +++ b/assets/home.js @@ -0,0 +1,344 @@ + +function pointInInterval(a, b, x) { + return a <= x && x <= b; +} + +function intervalIntersectsInterval(a, b, c, d) { + if (c <= a) { + return a <= d; + } + + return pointInInterval(a, b, c); +} + +function rectIntersectRect(x1, y1, x2, y2, x3, y3, x4, y4) { + return intervalIntersectsInterval(x1, x2, x3, x4) && intervalIntersectsInterval(y1, y2, y3, y4); +} + +function intersects(x1, y1, x2, y2, x3, y3, x4, y4) { + const a_minx = Math.min(x1, x2); + const a_maxx = Math.max(x1, x2); + const a_miny = Math.min(y1, y2); + const a_maxy = Math.max(y1, y2); + + const b_minx = Math.min(x3, x4); + const b_maxx = Math.max(x3, x4); + const b_miny = Math.min(y3, y4); + const b_maxy = Math.max(y3, y4); + + if (!rectIntersectRect(a_minx, a_miny, a_maxx, a_maxy, b_minx, b_miny, b_maxx, b_maxy)) + return false; + + const a_dx = x2 - x1; + const a_dy = y2 - y1; + + const b_dx = x4 - x3; + const b_dy = y4 - y3; + + const det = -b_dx * a_dy + a_dx * b_dy; + + if (Math.abs(det) === 0 && -a_dy * (x3 - x1) + a_dx * (y3 - y1) === 0) { + return y1 <= y3 <= y2 || y1 <= y4 <= y2; + } + + const s = (-a_dy * (x1 - x3) + a_dx * (y1 - y3)) / det; + const t = (+b_dx * (y1 - y3) - b_dy * (x1 - x3)) / det; + + return s >= 0 && s <= 1 && t >= 0 && t <= 1; +} + +const $canvas = document.querySelector('#circuit-pattern'); +let g = $canvas.getContext('2d'); + +let WIDTH = $canvas.offsetWidth; +let HEIGHT = $canvas.offsetHeight; + +const TS = 20; + +let ROWS = HEIGHT / TS; +let COLS = WIDTH / TS; + +function updateCanvasDimensions() { + g = $canvas.getContext('2d'); + + WIDTH = $canvas.offsetWidth; + HEIGHT = $canvas.offsetHeight; + + ROWS = HEIGHT / TS; + COLS = WIDTH / TS; + + $canvas.width = WIDTH * devicePixelRatio; + $canvas.height = HEIGHT * devicePixelRatio; + g.scale(2, 2); +} + +window.addEventListener('resize', () => updateCanvasDimensions()); +updateCanvasDimensions(); + +const state = { + wires: [], // :: [(Int, Int)] + nextWire: null, + nextWireIndex: 0, + nextSegmentLen: 0, + t: 0, +}; + +function randomFloat(min, max) { + return Math.random() * (max - min) + min; +} + +function randomInt(min, max) { + return Math.floor(Math.random() * (max - min + 1) + min); +} + +function randomChoice(list) { + return list[randomInt(0, list.length - 1)]; +} + +const DIRS = { + left: [-1, 1], + center: [0, 1], + right: [+1, 1], +}; + +const NEXT_DIRS = { + left: ['center'], + center: ['left', 'right'], + right: ['center'], +}; + +function canPlaceSegment([[p1x, p1y], [p2x, p2y]]) { + return state.wires.every(wire => { + return everyWireSegment(wire, ([[sx, sy], [lx, ly]]) => { + return !intersects(sx, sy, lx, ly, p1x, p1y, p2x, p2y); + }); + }) +} + +function everyWireSegment(wire, segmentFn) { + let [start, ...rest] = wire; + + return rest.every((pt) => { + const r = segmentFn([start, pt]); + start = pt; + return r; + }); +} + +function generateWire() { + const pieceCount = Math.pow(randomFloat(1, 2.5), 2) | 0; + const startPoint = [ + randomInt(0, COLS), + Math.pow(randomFloat(0, ROWS / 12), 2) | 0, + ]; + const sizes = Array(pieceCount).fill(0).map(() => randomInt(2, 4) | 0); + + const dirs = [randomChoice(Object.keys(DIRS))]; + for (let i = 0; i < pieceCount - 1; i++) { + const last = dirs[dirs.length - 1]; + dirs.push(randomChoice(NEXT_DIRS[last])); + } + + const wire = [startPoint]; + for (let i = 0; i < pieceCount; i++) { + const [lpx, lpy] = wire[wire.length - 1]; + const [dx, dy] = DIRS[dirs[i]]; + const s = sizes[i]; + + const s_bonus = (dirs[i] === 'center' ? s * 2 : s) | 0; + + wire.push([lpx + dx * s_bonus, lpy + dy * s_bonus]); + } + + const ok = everyWireSegment(wire, segment => { + return canPlaceSegment(segment); + }); + + if (!ok) + return null; + + return wire; +} + +// // LinearintERPolation +function lerp(a, b, t) { + return t * (b - a) + a; +} +function lerpPoint([x1, y1], [x2, y2], t) { + return [lerp(x1, x2, t), lerp(y1, y2, t)]; +} + +const CELLS_PER_SECOND = 15; + +let lastTime = new Date().getTime(); + +function update() { + const now = new Date().getTime(); + const delta = (now - lastTime) / 1000; + lastTime = now; + + if (state.nextWire) { + state.t += CELLS_PER_SECOND * delta; + + if (state.t >= 1.0) { + const [[x1, y1], [x2, y2]] = state.nextWire.slice(state.nextWireIndex - 2, state.nextWireIndex); + const dx = Math.abs(x2 - x1); + const dy = Math.abs(y2 - y1); + state.t = 0; + state.nextSegmentLen = Math.sqrt(dx * dx + dy * dy); + + state.nextWireIndex++; + } + + if (state.nextWireIndex > state.nextWire.length) { + state.wires.push(state.nextWire); + state.nextWire = null; + } + } else { + const wire = generateWire(); + if (wire) { + state.nextWire = wire; + state.nextWireIndex = 2; + + const [[x1, y1], [x2, y2]] = state.nextWire.slice(state.nextWireIndex - 2, state.nextWireIndex); + const dx = Math.abs(x2 - x1); + const dy = Math.abs(y2 - y1); + state.t = 0; + state.nextSegmentLen = Math.sqrt(dx * dx + dy * dy); + } + } +} + +function getColors() { + if (document.body.classList.contains('dark-mode')) { + return { + BACKGROUND: '#282828', + // CIRCUIT: '#202020', + CIRCUIT: '#38302e', + } + } else { + return { + BACKGROUND: '#eaeaea', + CIRCUIT: '#d4d4d4', + }; + } +} + +function render() { + const { BACKGROUND, CIRCUIT } = getColors(); + + g.clearRect(0, 0, WIDTH, HEIGHT); + + g.strokeStyle = CIRCUIT; + g.lineWidth = 3; + + state.wires.forEach(wire => { + const [[sx, sy], ...rest] = wire; + + function renderDot(x, y) { + if (y === 0) { + return; + } + + g.beginPath(); + g.ellipse(x * TS, y * TS, TS / 5, TS / 5, 0, 0, Math.PI * 2); + g.stroke(); + } + + renderDot(sx, sy); + + g.beginPath(); + g.moveTo(sx * TS, sy * TS); + rest.forEach(([x, y]) => { + g.lineTo(x * TS, y * TS); + }); + g.stroke(); + + const [lx, ly] = wire[wire.length - 1]; + + renderDot(lx, ly); + + [wire[0], wire[wire.length - 1]].forEach(([x, y]) => { + if (y === 0) { + return; + } + + g.fillStyle = BACKGROUND; + g.beginPath(); + g.ellipse(x * TS, y * TS, TS / 5 - 1, TS / 5 - 1, 0, 0, Math.PI * 2); + g.fill(); + }); + }); + + if (state.nextWire) { + const wirePart = state.nextWire.slice(0, state.nextWireIndex); + const wire = [ + ...wirePart.slice(0, wirePart.length - 1), + lerpPoint( + wirePart[wirePart.length - 2], + wirePart[wirePart.length - 1], + state.t + ) + ]; + const [[sx, sy], ...rest] = wire; + + function renderDot(x, y) { + if (y === 0) { + return; + } + + g.beginPath(); + g.ellipse(x * TS, y * TS, TS / 5, TS / 5, 0, 0, Math.PI * 2); + g.stroke(); + } + + renderDot(sx, sy); + + g.beginPath(); + g.moveTo(sx * TS, sy * TS); + rest.forEach(([x, y]) => { + g.lineTo(x * TS, y * TS); + }); + g.stroke(); + + const [lx, ly] = wire[wire.length - 1]; + + renderDot(lx, ly); + + [wire[0], wire[wire.length - 1]].forEach(([x, y]) => { + if (y === 0) { + return; + } + + g.fillStyle = BACKGROUND; + g.beginPath(); + g.ellipse(x * TS, y * TS, TS / 5 - 1, TS / 5 - 1, 0, 0, Math.PI * 2); + g.fill(); + }); + } + + requestAnimationFrame(render); +} + + +// g.translate(0.5, 0.5); + +window.addEventListener('DOMContentLoaded', () => { + // $canvas.classList.remove('hide'); + + render(); + + let i = 20; + while (i > 0) { + const wire = generateWire(); + + if (wire) { + state.wires.push(wire); + i--; + } + } + + setInterval(() => { + update(); + }, 1000 / 30); +}) \ No newline at end of file diff --git a/assets/style.css b/assets/style.css index 5048b22..3c1d529 100644 --- a/assets/style.css +++ b/assets/style.css @@ -25,7 +25,7 @@ } * { - box-sizing: border-box; + box-sizing: border-box; } html, body { @@ -383,6 +383,24 @@ a:hover { gap: 2rem; } +.page-home canvas { + position: absolute; + width: 100vw; + height: 100vh; + top: 0; + right: 0; + + pointer-events: none; + z-index: -1; + + transition: opacity 1000ms ease-in-out; + opacity: 1; +} + +.page-home canvas.hide { + opacity: 0; +} + .page-home .super .block.text { max-width: 40ch; font-size: 2.5vmin; diff --git a/views/home.html b/views/home.html index a638eca..6f67e32 100644 --- a/views/home.html +++ b/views/home.html @@ -3,6 +3,8 @@ {{define "title"}}PHC • phc.dm.xxxxx.xx{{end}} {{define "body"}} + +
@@ -95,4 +97,6 @@
+ + {{end}} \ No newline at end of file