Update frontend

feat/db
Antonio De Lucreziis 2 years ago
parent c9d02d22dd
commit 1f90dacbdd

@ -12,10 +12,14 @@
"license": "MIT",
"devDependencies": {
"@rollup/plugin-node-resolve": "^13.3.0",
"@rollup/plugin-typescript": "^8.3.4",
"esbuild": "^0.15.5",
"npm-run-all": "^4.1.5",
"rollup": "^2.75.3",
"rollup-plugin-esbuild": "^4.9.3",
"rollup-plugin-terser": "^7.0.2",
"sass": "^1.52.3"
"sass": "^1.52.3",
"typescript": "^4.7.4"
},
"dependencies": {
"alpinejs": "^3.10.2",

@ -2,12 +2,16 @@ lockfileVersion: 5.4
specifiers:
'@rollup/plugin-node-resolve': ^13.3.0
'@rollup/plugin-typescript': ^8.3.4
alpinejs: ^3.10.2
esbuild: ^0.15.5
fuse.js: ^6.6.2
npm-run-all: ^4.1.5
rollup: ^2.75.3
rollup-plugin-esbuild: ^4.9.3
rollup-plugin-terser: ^7.0.2
sass: ^1.52.3
typescript: ^4.7.4
dependencies:
alpinejs: 3.10.3
@ -15,10 +19,14 @@ dependencies:
devDependencies:
'@rollup/plugin-node-resolve': 13.3.0_rollup@2.78.0
'@rollup/plugin-typescript': 8.3.4_nm5mlcuxlwr6samvke7b2fz27i
esbuild: 0.15.5
npm-run-all: 4.1.5
rollup: 2.78.0
rollup-plugin-esbuild: 4.9.3_g2b53jqudjimruv6spqg4ieafm
rollup-plugin-terser: 7.0.2_rollup@2.78.0
sass: 1.54.4
typescript: 4.7.4
packages:
@ -43,6 +51,15 @@ packages:
js-tokens: 4.0.0
dev: true
/@esbuild/linux-loong64/0.15.5:
resolution: {integrity: sha512-UHkDFCfSGTuXq08oQltXxSZmH1TXyWsL+4QhZDWvvLl6mEJQqk3u7/wq1LjhrrAXYIllaTtRSzUXl4Olkf2J8A==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@jridgewell/gen-mapping/0.3.2:
resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==}
engines: {node: '>=6.0.0'}
@ -95,6 +112,23 @@ packages:
rollup: 2.78.0
dev: true
/@rollup/plugin-typescript/8.3.4_nm5mlcuxlwr6samvke7b2fz27i:
resolution: {integrity: sha512-wt7JnYE9antX6BOXtsxGoeVSu4dZfw0dU3xykfOQ4hC3EddxRbVG/K0xiY1Wup7QOHJcjLYXWAn0Kx9Z1SBHHg==}
engines: {node: '>=8.0.0'}
peerDependencies:
rollup: ^2.14.0
tslib: '*'
typescript: '>=3.7.0'
peerDependenciesMeta:
tslib:
optional: true
dependencies:
'@rollup/pluginutils': 3.1.0_rollup@2.78.0
resolve: 1.22.1
rollup: 2.78.0
typescript: 4.7.4
dev: true
/@rollup/pluginutils/3.1.0_rollup@2.78.0:
resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==}
engines: {node: '>= 8.0.0'}
@ -107,6 +141,14 @@ packages:
rollup: 2.78.0
dev: true
/@rollup/pluginutils/4.2.1:
resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
engines: {node: '>= 8.0.0'}
dependencies:
estree-walker: 2.0.2
picomatch: 2.3.1
dev: true
/@types/estree/0.0.39:
resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==}
dev: true
@ -250,6 +292,18 @@ packages:
which: 1.3.1
dev: true
/debug/4.3.4:
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
dependencies:
ms: 2.1.2
dev: true
/deepmerge/4.2.2:
resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==}
engines: {node: '>=0.10.0'}
@ -298,6 +352,10 @@ packages:
unbox-primitive: 1.0.2
dev: true
/es-module-lexer/0.9.3:
resolution: {integrity: sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==}
dev: true
/es-to-primitive/1.2.1:
resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
engines: {node: '>= 0.4'}
@ -307,6 +365,215 @@ packages:
is-symbol: 1.0.4
dev: true
/esbuild-android-64/0.15.5:
resolution: {integrity: sha512-dYPPkiGNskvZqmIK29OPxolyY3tp+c47+Fsc2WYSOVjEPWNCHNyqhtFqQadcXMJDQt8eN0NMDukbyQgFcHquXg==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
requiresBuild: true
dev: true
optional: true
/esbuild-android-arm64/0.15.5:
resolution: {integrity: sha512-YyEkaQl08ze3cBzI/4Cm1S+rVh8HMOpCdq8B78JLbNFHhzi4NixVN93xDrHZLztlocEYqi45rHHCgA8kZFidFg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
requiresBuild: true
dev: true
optional: true
/esbuild-darwin-64/0.15.5:
resolution: {integrity: sha512-Cr0iIqnWKx3ZTvDUAzG0H/u9dWjLE4c2gTtRLz4pqOBGjfjqdcZSfAObFzKTInLLSmD0ZV1I/mshhPoYSBMMCQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/esbuild-darwin-arm64/0.15.5:
resolution: {integrity: sha512-WIfQkocGtFrz7vCu44ypY5YmiFXpsxvz2xqwe688jFfSVCnUsCn2qkEVDo7gT8EpsLOz1J/OmqjExePL1dr1Kg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/esbuild-freebsd-64/0.15.5:
resolution: {integrity: sha512-M5/EfzV2RsMd/wqwR18CELcenZ8+fFxQAAEO7TJKDmP3knhWSbD72ILzrXFMMwshlPAS1ShCZ90jsxkm+8FlaA==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/esbuild-freebsd-arm64/0.15.5:
resolution: {integrity: sha512-2JQQ5Qs9J0440F/n/aUBNvY6lTo4XP/4lt1TwDfHuo0DY3w5++anw+jTjfouLzbJmFFiwmX7SmUhMnysocx96w==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-32/0.15.5:
resolution: {integrity: sha512-gO9vNnIN0FTUGjvTFucIXtBSr1Woymmx/aHQtuU+2OllGU6YFLs99960UD4Dib1kFovVgs59MTXwpFdVoSMZoQ==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-64/0.15.5:
resolution: {integrity: sha512-ne0GFdNLsm4veXbTnYAWjbx3shpNKZJUd6XpNbKNUZaNllDZfYQt0/zRqOg0sc7O8GQ+PjSMv9IpIEULXVTVmg==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-arm/0.15.5:
resolution: {integrity: sha512-wvAoHEN+gJ/22gnvhZnS/+2H14HyAxM07m59RSLn3iXrQsdS518jnEWRBnJz3fR6BJa+VUTo0NxYjGaNt7RA7Q==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-arm64/0.15.5:
resolution: {integrity: sha512-7EgFyP2zjO065XTfdCxiXVEk+f83RQ1JsryN1X/VSX2li9rnHAt2swRbpoz5Vlrl6qjHrCmq5b6yxD13z6RheA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-mips64le/0.15.5:
resolution: {integrity: sha512-KdnSkHxWrJ6Y40ABu+ipTZeRhFtc8dowGyFsZY5prsmMSr1ZTG9zQawguN4/tunJ0wy3+kD54GaGwdcpwWAvZQ==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-ppc64le/0.15.5:
resolution: {integrity: sha512-QdRHGeZ2ykl5P0KRmfGBZIHmqcwIsUKWmmpZTOq573jRWwmpfRmS7xOhmDHBj9pxv+6qRMH8tLr2fe+ZKQvCYw==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-riscv64/0.15.5:
resolution: {integrity: sha512-p+WE6RX+jNILsf+exR29DwgV6B73khEQV0qWUbzxaycxawZ8NE0wA6HnnTxbiw5f4Gx9sJDUBemh9v49lKOORA==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-s390x/0.15.5:
resolution: {integrity: sha512-J2ngOB4cNzmqLHh6TYMM/ips8aoZIuzxJnDdWutBw5482jGXiOzsPoEF4j2WJ2mGnm7FBCO4StGcwzOgic70JQ==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-netbsd-64/0.15.5:
resolution: {integrity: sha512-MmKUYGDizYjFia0Rwt8oOgmiFH7zaYlsoQ3tIOfPxOqLssAsEgG0MUdRDm5lliqjiuoog8LyDu9srQk5YwWF3w==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
requiresBuild: true
dev: true
optional: true
/esbuild-openbsd-64/0.15.5:
resolution: {integrity: sha512-2mMFfkLk3oPWfopA9Plj4hyhqHNuGyp5KQyTT9Rc8hFd8wAn5ZrbJg+gNcLMo2yzf8Uiu0RT6G9B15YN9WQyMA==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
requiresBuild: true
dev: true
optional: true
/esbuild-sunos-64/0.15.5:
resolution: {integrity: sha512-2sIzhMUfLNoD+rdmV6AacilCHSxZIoGAU2oT7XmJ0lXcZWnCvCtObvO6D4puxX9YRE97GodciRGDLBaiC6x1SA==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
requiresBuild: true
dev: true
optional: true
/esbuild-windows-32/0.15.5:
resolution: {integrity: sha512-e+duNED9UBop7Vnlap6XKedA/53lIi12xv2ebeNS4gFmu7aKyTrok7DPIZyU5w/ftHD4MUDs5PJUkQPP9xJRzg==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: true
optional: true
/esbuild-windows-64/0.15.5:
resolution: {integrity: sha512-v+PjvNtSASHOjPDMIai9Yi+aP+Vwox+3WVdg2JB8N9aivJ7lyhp4NVU+J0MV2OkWFPnVO8AE/7xH+72ibUUEnw==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/esbuild-windows-arm64/0.15.5:
resolution: {integrity: sha512-Yz8w/D8CUPYstvVQujByu6mlf48lKmXkq6bkeSZZxTA626efQOJb26aDGLzmFWx6eg/FwrXgt6SZs9V8Pwy/aA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/esbuild/0.15.5:
resolution: {integrity: sha512-VSf6S1QVqvxfIsSKb3UKr3VhUCis7wgDbtF4Vd9z84UJr05/Sp2fRKmzC+CSPG/dNAPPJZ0BTBLTT1Fhd6N9Gg==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
'@esbuild/linux-loong64': 0.15.5
esbuild-android-64: 0.15.5
esbuild-android-arm64: 0.15.5
esbuild-darwin-64: 0.15.5
esbuild-darwin-arm64: 0.15.5
esbuild-freebsd-64: 0.15.5
esbuild-freebsd-arm64: 0.15.5
esbuild-linux-32: 0.15.5
esbuild-linux-64: 0.15.5
esbuild-linux-arm: 0.15.5
esbuild-linux-arm64: 0.15.5
esbuild-linux-mips64le: 0.15.5
esbuild-linux-ppc64le: 0.15.5
esbuild-linux-riscv64: 0.15.5
esbuild-linux-s390x: 0.15.5
esbuild-netbsd-64: 0.15.5
esbuild-openbsd-64: 0.15.5
esbuild-sunos-64: 0.15.5
esbuild-windows-32: 0.15.5
esbuild-windows-64: 0.15.5
esbuild-windows-arm64: 0.15.5
dev: true
/escape-string-regexp/1.0.5:
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
engines: {node: '>=0.8.0'}
@ -316,6 +583,10 @@ packages:
resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==}
dev: true
/estree-walker/2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
dev: true
/fill-range/7.0.1:
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
engines: {node: '>=8'}
@ -567,6 +838,11 @@ packages:
supports-color: 7.2.0
dev: true
/joycon/3.1.1:
resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
engines: {node: '>=10'}
dev: true
/js-tokens/4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
dev: true
@ -575,6 +851,10 @@ packages:
resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==}
dev: true
/jsonc-parser/3.1.0:
resolution: {integrity: sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==}
dev: true
/load-json-file/4.0.0:
resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==}
engines: {node: '>=4'}
@ -600,6 +880,10 @@ packages:
brace-expansion: 1.1.11
dev: true
/ms/2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
dev: true
/nice-try/1.0.5:
resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
dev: true
@ -733,6 +1017,24 @@ packages:
supports-preserve-symlinks-flag: 1.0.0
dev: true
/rollup-plugin-esbuild/4.9.3_g2b53jqudjimruv6spqg4ieafm:
resolution: {integrity: sha512-bxfUNYTa9Tw/4kdFfT9gtidDtqXyRdCW11ctZM7D8houCCVqp5qHzQF7hhIr31rqMA0APbG47fgVbbCGXgM49Q==}
engines: {node: '>=12'}
peerDependencies:
esbuild: '>=0.10.1'
rollup: ^1.20.0 || ^2.0.0
dependencies:
'@rollup/pluginutils': 4.2.1
debug: 4.3.4
es-module-lexer: 0.9.3
esbuild: 0.15.5
joycon: 3.1.1
jsonc-parser: 3.1.0
rollup: 2.78.0
transitivePeerDependencies:
- supports-color
dev: true
/rollup-plugin-terser/7.0.2_rollup@2.78.0:
resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==}
peerDependencies:
@ -908,6 +1210,12 @@ packages:
is-number: 7.0.0
dev: true
/typescript/4.7.4:
resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==}
engines: {node: '>=4.2.0'}
hasBin: true
dev: true
/unbox-primitive/1.0.2:
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
dependencies:

@ -1,7 +1,16 @@
import { defineConfig } from 'rollup'
import { terser } from 'rollup-plugin-terser'
import esbuild from 'rollup-plugin-esbuild'
export default defineConfig([
{
input: 'src/base.js',
output: {
file: 'out/base.min.js',
format: 'iife',
},
plugins: [terser()],
},
{
input: 'src/utenti.js',
external: ['alpinejs', 'fuse.js'], // libraries to not bundle
@ -28,4 +37,12 @@ export default defineConfig([
},
plugins: [terser()],
},
{
input: 'src/homepage-art.ts',
output: {
file: 'out/homepage-art.min.js',
format: 'iife',
},
plugins: [esbuild({ minify: true })],
},
])

@ -0,0 +1,41 @@
document.addEventListener('DOMContentLoaded', function () {
renderMathInElement(document.body, {
delimiters: [
{ left: '$', right: '$', display: false },
{ left: '$$', right: '$$', display: true },
{ left: '\\(', right: '\\)', display: false },
{ left: '\\[', right: '\\]', display: true },
],
})
})
document.addEventListener('DOMContentLoaded', () => {
const $toggle = document.querySelector('#toggle-dark-mode')
const $toggleIcon = document.querySelector('#toggle-dark-mode i')
// Loads preferred dark from from localStorage or defaults to media query.
let prefersDarkMode =
localStorage.getItem('theme') !== undefined
? localStorage.getItem('theme') === 'dark'
: window.matchMedia('(prefers-color-scheme: dark)').matches
function storePrefersDarkMode(mode) {
prefersDarkMode = mode
localStorage.setItem('theme', mode ? 'dark' : 'light')
}
function displayToggle() {
document.body.classList.toggle('dark-mode', prefersDarkMode)
$toggleIcon.classList.toggle('fa-moon', prefersDarkMode)
$toggleIcon.classList.toggle('fa-sun', !prefersDarkMode)
document.dispatchEvent(new CustomEvent('theme:change'))
}
$toggle.addEventListener('click', () => {
storePrefersDarkMode(!prefersDarkMode)
displayToggle()
})
displayToggle()
})

@ -0,0 +1,378 @@
type Point2i = [number, number]
type WireDirection = 'down-left' | 'down' | 'down-right'
type TipPosition = false | 'begin' | 'end' | 'begin-end'
type WirePiece = {
direction: WireDirection
lerp: number
tipPosition: TipPosition
}
type LatticePoint = string
function toLatticePoint(x: number, y: number): LatticePoint {
return `${x | 0},${y | 0}`
}
function fromLatticePoint(p: LatticePoint): Point2i {
const [x, y] = p.split(',').map(s => parseInt(s))
return [x, y]
}
type WireNode = { point: Point2i; direction: WireDirection }
type Wire = WireNode[]
type World = {
dimensions: Point2i
wirePieces: { [point: LatticePoint]: WirePiece }
wiresQueue: { wire: Wire; cursor: number }[]
}
function randomChoice<T>(array: T[]): T {
return array[Math.floor(Math.random() * array.length)]
}
const randomDirection = (): WireDirection => randomChoice(['down', 'down-left', 'down-right'])
const nextPoint = ([x, y]: [number, number], direction: WireDirection): [number, number] => {
if (direction === 'down') return [x, y + 1]
if (direction === 'down-left') return [x - 1, y + 1]
if (direction === 'down-right') return [x + 1, y + 1]
throw 'invalid'
}
function checkPoint(world: World, [x, y]: [number, number]): boolean {
return !!world.wirePieces[toLatticePoint(x, y)]
}
function wireIntersects(world: World, wire: Wire): boolean {
return wire.some(({ point: [x, y], direction }) => {
// TODO: The point check actually "doubly" depends on direction
if (direction === 'down') {
return checkPoint(world, [x, y]) || checkPoint(world, [x, y + 1])
}
if (direction === 'down-left') {
return checkPoint(world, [x, y]) || checkPoint(world, [x - 1, y])
}
if (direction === 'down-right') {
return checkPoint(world, [x, y]) || checkPoint(world, [x + 1, y])
}
return false
})
}
function generateWire(world: World): Wire {
const [w, h] = world.dimensions
const randomPoint = (): [number, number] => [
Math.floor(Math.random() * w),
Math.floor(Math.pow(Math.random(), 2) * h * 0.5),
]
const wireLength = 3 + Math.floor(Math.random() * 10)
const wire: Wire = [
{
point: randomPoint(),
direction: randomDirection(),
},
]
let prev = wire[0]
let dir = prev.direction
for (let i = 0; i < wireLength; i++) {
const p = nextPoint(prev.point, dir)
if (Math.random() < 0.325) {
// change direction
if (dir === 'down') {
dir = randomChoice(['down-left', 'down-right'])
} else {
dir = 'down'
}
}
wire.push({
point: p,
direction: dir,
})
prev = wire[wire.length - 1]
}
return wire
}
function getTheme() {
if (document.body.classList.contains('dark-mode')) {
return {
backgroundColor: '#282828',
circuitColor: '#38302e',
}
} else {
return {
backgroundColor: '#eaeaea',
circuitColor: '#d4d4d4',
}
}
}
class Art {
static CELL_SIZE = 28
static TIP_RADIUS = 4
static WIRE_LERP_SPEED = 25 // units / seconds
renewGraphicsContext: boolean = true
dirty: boolean
world: World
constructor($canvas: HTMLCanvasElement) {
let g: CanvasRenderingContext2D
let unMount = this.setup($canvas)
window.addEventListener('resize', () => {
this.renewGraphicsContext = true
unMount()
unMount = this.setup($canvas)
})
document.addEventListener('theme:change', () => {
this.dirty = true
})
const renderFn = () => {
if (this.renewGraphicsContext) {
$canvas.width = $canvas.offsetWidth * devicePixelRatio
$canvas.height = $canvas.offsetHeight * devicePixelRatio
g = $canvas.getContext('2d')!
g.scale(devicePixelRatio, devicePixelRatio)
}
if (this.dirty || this.renewGraphicsContext) {
console.log('Rendering')
this.render(g, $canvas.offsetWidth, $canvas.offsetHeight)
}
this.dirty = false
this.renewGraphicsContext = false
requestAnimationFrame(renderFn)
}
renderFn()
}
setup($canvas: HTMLCanvasElement) {
this.world = {
dimensions: [
Math.ceil($canvas.offsetWidth / Art.CELL_SIZE),
Math.ceil($canvas.offsetHeight / Art.CELL_SIZE),
],
wirePieces: {},
wiresQueue: [],
}
let failedTries = 0
const wireGeneratorTimer = setInterval(() => {
if (this.world.wiresQueue.length > 0) {
return
}
// console.log('Trying to generate wire')
if (failedTries > 400) {
console.log('Stopped generating wires')
clearInterval(wireGeneratorTimer)
return
}
const wire = generateWire(this.world)
if (!wireIntersects(this.world, wire)) {
failedTries = 0
this.world.wiresQueue.push({ wire, cursor: 0 })
} else {
failedTries++
}
}, 10)
let pieceLerpBeginTime = new Date().getTime()
const wireQueueTimer = setInterval(() => {
if (this.world.wiresQueue.length > 0) {
// console.log('Interpolating queued wire')
// get top wire to add
const wireInterp = this.world.wiresQueue[0]
if (wireInterp.cursor < wireInterp.wire.length) {
const currentNode = wireInterp.wire[wireInterp.cursor]
const pieceLerpEndTime = pieceLerpBeginTime + 1000 / Art.WIRE_LERP_SPEED
const now = new Date().getTime()
if (now > pieceLerpEndTime) {
wireInterp.cursor++
pieceLerpBeginTime = new Date().getTime()
this.world.wirePieces[toLatticePoint(...currentNode.point)] = {
direction: currentNode.direction,
lerp: 1,
tipPosition:
wireInterp.cursor === 1
? 'begin'
: wireInterp.cursor === wireInterp.wire.length
? 'end'
: false,
}
this.dirty = true
return
}
const lerp = ((now - pieceLerpBeginTime) / 1000) * Art.WIRE_LERP_SPEED
this.world.wirePieces[toLatticePoint(...currentNode.point)] = {
...currentNode,
tipPosition: wireInterp.cursor === 0 ? 'begin-end' : 'end',
lerp,
}
this.dirty = true
} else {
this.world.wiresQueue.splice(0, 1)
}
}
}, 1000 / 60)
const unMount = () => {
clearInterval(wireGeneratorTimer)
clearInterval(wireQueueTimer)
}
// document.addEventListener('keypress', e => {
// if (e.key === 'r') {
// unMount()
// this.setup($canvas)
// }
// })
return unMount
}
render(g: CanvasRenderingContext2D, width: number, height: number) {
g.clearRect(0, 0, width, height)
const { backgroundColor, circuitColor } = getTheme()
// Grid
// g.lineWidth = 1
// g.strokeStyle = '#ddd'
// g.beginPath()
// for (let i = 0; i < height / Art.CELL_SIZE; i++) {
// g.moveTo(0, i * Art.CELL_SIZE)
// g.lineTo(width, i * Art.CELL_SIZE)
// }
// for (let j = 0; j < width / Art.CELL_SIZE; j++) {
// g.moveTo(j * Art.CELL_SIZE, 0)
// g.lineTo(j * Art.CELL_SIZE, height)
// }
// g.stroke()
g.lineWidth = 3
g.strokeStyle = circuitColor
g.lineCap = 'round'
g.lineJoin = 'round'
for (const [lp, piece] of Object.entries(this.world.wirePieces)) {
const [x, y] = fromLatticePoint(lp)
g.beginPath()
g.moveTo(x * Art.CELL_SIZE, y * Art.CELL_SIZE)
switch (piece.direction) {
case 'down-left':
g.lineTo((x - piece.lerp) * Art.CELL_SIZE, (y + piece.lerp) * Art.CELL_SIZE)
break
case 'down':
g.lineTo(x * Art.CELL_SIZE, (y + piece.lerp) * Art.CELL_SIZE)
break
case 'down-right':
g.lineTo((x + piece.lerp) * Art.CELL_SIZE, (y + piece.lerp) * Art.CELL_SIZE)
break
}
g.stroke()
}
for (const [lp, piece] of Object.entries(this.world.wirePieces)) {
const [x, y] = fromLatticePoint(lp)
const drawTip = () => {
if (
y !== 0 &&
(piece.tipPosition === 'begin' || piece.tipPosition === 'begin-end')
) {
switch (piece.direction) {
case 'down-left':
{
const cx = x * Art.CELL_SIZE
const cy = y * Art.CELL_SIZE
g.ellipse(cx, cy, Art.TIP_RADIUS, Art.TIP_RADIUS, 0, 0, 2 * Math.PI)
}
break
case 'down':
{
const cx = x * Art.CELL_SIZE
const cy = y * Art.CELL_SIZE
g.ellipse(cx, cy, Art.TIP_RADIUS, Art.TIP_RADIUS, 0, 0, 2 * Math.PI)
}
break
case 'down-right':
{
const cx = x * Art.CELL_SIZE
const cy = y * Art.CELL_SIZE
g.ellipse(cx, cy, Art.TIP_RADIUS, Art.TIP_RADIUS, 0, 0, 2 * Math.PI)
}
break
}
}
if (piece.tipPosition === 'end' || piece.tipPosition === 'begin-end') {
switch (piece.direction) {
case 'down-left':
{
const cx = (x - piece.lerp) * Art.CELL_SIZE
const cy = (y + piece.lerp) * Art.CELL_SIZE
g.ellipse(cx, cy, Art.TIP_RADIUS, Art.TIP_RADIUS, 0, 0, 2 * Math.PI)
}
break
case 'down':
{
const cx = x * Art.CELL_SIZE
const cy = (y + piece.lerp) * Art.CELL_SIZE
g.ellipse(cx, cy, Art.TIP_RADIUS, Art.TIP_RADIUS, 0, 0, 2 * Math.PI)
}
break
case 'down-right':
{
const cx = (x + piece.lerp) * Art.CELL_SIZE
const cy = (y + piece.lerp) * Art.CELL_SIZE
g.ellipse(cx, cy, Art.TIP_RADIUS, Art.TIP_RADIUS, 0, 0, 2 * Math.PI)
}
break
}
}
}
if (piece.tipPosition) {
g.fillStyle = backgroundColor
g.beginPath()
drawTip()
g.fill()
g.beginPath()
drawTip()
g.stroke()
}
}
}
}
const $canvas = document.querySelector('#wires-animation') as HTMLCanvasElement
new Art($canvas)

@ -0,0 +1,9 @@
{
"compilerOptions": {
"target": "esnext",
"noImplicitAny": true,
"sourceMap": true,
"noEmitOnError": true
},
"filesGlob": ["./src/**/*.ts"]
}

@ -17,44 +17,8 @@
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js"
integrity="sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa"
crossorigin="anonymous"></script>
<script>
document.addEventListener("DOMContentLoaded", function () {
renderMathInElement(document.body, {
delimiters: [
{ left: "$", right: "$", display: false },
{ left: "$$", right: "$$", display: true },
{ left: "\\(", right: "\\)", display: false },
{ left: "\\[", right: "\\]", display: true }
]
});
});
document.addEventListener('DOMContentLoaded', () => {
const $toggle = document.querySelector('#toggle-dark-mode');
const $toggleIcon = document.querySelector('#toggle-dark-mode i');
// Loads prefered dark from from localStorage or defaults to media query.
let prefersDarkMode = localStorage.getItem('theme') !== undefined ? localStorage.getItem('theme') === 'dark' : window.matchMedia('(prefers-color-scheme: dark)').matches;
function storePrefersDarkMode(mode) {
prefersDarkMode = mode;
localStorage.setItem('theme', mode ? 'dark' : 'light');
}
function displayToggle() {
document.body.classList.toggle('dark-mode', prefersDarkMode);
$toggleIcon.classList.toggle('fa-moon', prefersDarkMode);
$toggleIcon.classList.toggle('fa-sun', !prefersDarkMode);
}
$toggle.addEventListener('click', () => {
storePrefersDarkMode(!prefersDarkMode);
displayToggle();
});
displayToggle();
});
</script>
<script src="/public/base.min.js"></script>
<link rel="stylesheet" href="/public/style.css">
<link rel="stylesheet" href="/public/theme-dark.css">

@ -3,7 +3,7 @@
{{define "title"}}Home &bull; PHC{{end}}
{{define "body"}}
<canvas id="circuit-pattern"></canvas>
<canvas id="wires-animation"></canvas>
<!-- Non avevo molta fantasia per il nome di questa classe -->
<section class="super">
@ -103,5 +103,5 @@
</div>
</section>
<script src="/public/home.js"></script>
<script src="/public/homepage-art.min.js"></script>
{{end}}

Loading…
Cancel
Save