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"}}
+
+
+
+
{{end}}
\ No newline at end of file