diff --git a/.eslintrc.json b/.eslintrc.json index c7a4a95..eaa6716 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -5,7 +5,8 @@ }, "extends": [ "eslint:recommended", - "plugin:@typescript-eslint/recommended" + "plugin:@typescript-eslint/recommended", + "prettier" ], "parser": "@typescript-eslint/parser", "parserOptions": { @@ -13,13 +14,14 @@ "sourceType": "module" }, "plugins": [ - "@typescript-eslint" + "@typescript-eslint", + "prettier" ], "rules": { - "indent": [ - "error", - 2 - ], + // "indent": [ + // "error", + // 2 + // ], "linebreak-style": [ "error", "windows" @@ -31,6 +33,13 @@ "semi": [ "error", "always" + ], + "prettier/prettier": [ + "error", + { + "endOfLine": "auto", + "tabWidth": 2 + } ] } } diff --git a/.lintstagedrc.js b/.lintstagedrc.js index f7cc9db..5f65ace 100644 --- a/.lintstagedrc.js +++ b/.lintstagedrc.js @@ -7,5 +7,8 @@ export default { '**/*.(js|json)': () => [ `npm run prettier`, `npm run lint`, + ], + '**/*.css': () => [ + `npm run prettier`, ] }; \ No newline at end of file diff --git a/index.html b/index.html index d7939a1..fa8d722 100644 --- a/index.html +++ b/index.html @@ -2,10 +2,12 @@ + Bunny Snek Nether Network Map +
diff --git a/package-lock.json b/package-lock.json index 77651fd..ebf0c8a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,8 @@ "@typescript-eslint/eslint-plugin": "^6.7.2", "@typescript-eslint/parser": "^6.7.2", "eslint": "^8.49.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0", "husky": "^8.0.3", "lint-staged": "^14.0.1", "prettier": "^3.0.3", @@ -509,6 +511,26 @@ "node": ">= 8" } }, + "node_modules/@pkgr/utils": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", + "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.3.0", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.6.0" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@types/dat.gui": { "version": "0.7.10", "resolved": "https://registry.npmjs.org/@types/dat.gui/-/dat.gui-0.7.10.tgz", @@ -849,6 +871,27 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.44" + }, + "engines": { + "node": ">= 5.10.0" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -871,6 +914,21 @@ "node": ">=8" } }, + "node_modules/bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "dependencies": { + "run-applescript": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1008,6 +1066,52 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "dependencies": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "dependencies": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -1147,6 +1251,47 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-config-prettier": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz", + "integrity": "sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, "node_modules/eslint-scope": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", @@ -1269,6 +1414,12 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, "node_modules/fast-glob": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", @@ -1572,6 +1723,21 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1605,6 +1771,24 @@ "node": ">=0.10.0" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -1635,6 +1819,33 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -1984,6 +2195,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "dependencies": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -2161,6 +2390,18 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", @@ -2286,6 +2527,110 @@ "fsevents": "~2.3.2" } }, + "node_modules/run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/run-applescript/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/run-applescript/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/run-applescript/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/run-applescript/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-applescript/node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -2498,6 +2843,22 @@ "node": ">=8" } }, + "node_modules/synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "dependencies": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -2509,6 +2870,18 @@ "resolved": "https://registry.npmjs.org/three/-/three-0.156.1.tgz", "integrity": "sha512-kP7H0FK9d/k6t/XvQ9FO6i+QrePoDcNhwl0I02+wmUJRNSLCUIDMcfObnzQvxb37/0Uc9TDT0T1HgsRRrO6SYQ==" }, + "node_modules/titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2533,6 +2906,12 @@ "typescript": ">=4.2.0" } }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -2570,6 +2949,15 @@ "node": ">=14.17" } }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -2987,6 +3375,20 @@ "fastq": "^1.6.0" } }, + "@pkgr/utils": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", + "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.3.0", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.6.0" + } + }, "@types/dat.gui": { "version": "0.7.10", "resolved": "https://registry.npmjs.org/@types/dat.gui/-/dat.gui-0.7.10.tgz", @@ -3204,6 +3606,21 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true + }, + "bplist-parser": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", + "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", + "dev": true, + "requires": { + "big-integer": "^1.6.44" + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3223,6 +3640,15 @@ "fill-range": "^7.0.1" } }, + "bundle-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", + "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", + "dev": true, + "requires": { + "run-applescript": "^5.0.0" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -3322,6 +3748,34 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "default-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", + "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", + "dev": true, + "requires": { + "bundle-name": "^3.0.0", + "default-browser-id": "^3.0.0", + "execa": "^7.1.1", + "titleize": "^3.0.0" + } + }, + "default-browser-id": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", + "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", + "dev": true, + "requires": { + "bplist-parser": "^0.2.0", + "untildify": "^4.0.0" + } + }, + "define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true + }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -3433,6 +3887,23 @@ "text-table": "^0.2.0" } }, + "eslint-config-prettier": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", + "dev": true, + "requires": {} + }, + "eslint-plugin-prettier": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz", + "integrity": "sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.5" + } + }, "eslint-scope": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", @@ -3519,6 +3990,12 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, "fast-glob": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", @@ -3742,6 +4219,12 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -3763,6 +4246,15 @@ "is-extglob": "^2.1.1" } }, + "is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "requires": { + "is-docker": "^3.0.0" + } + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -3781,6 +4273,23 @@ "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + }, + "dependencies": { + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + } + } + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -4029,6 +4538,18 @@ "mimic-fn": "^4.0.0" } }, + "open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "requires": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + } + }, "optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -4135,6 +4656,15 @@ "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", "dev": true }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, "punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", @@ -4210,6 +4740,76 @@ "fsevents": "~2.3.2" } }, + "run-applescript": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", + "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", + "dev": true, + "requires": { + "execa": "^5.0.0" + }, + "dependencies": { + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + } + } + }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -4343,6 +4943,16 @@ "has-flag": "^4.0.0" } }, + "synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "requires": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -4354,6 +4964,12 @@ "resolved": "https://registry.npmjs.org/three/-/three-0.156.1.tgz", "integrity": "sha512-kP7H0FK9d/k6t/XvQ9FO6i+QrePoDcNhwl0I02+wmUJRNSLCUIDMcfObnzQvxb37/0Uc9TDT0T1HgsRRrO6SYQ==" }, + "titleize": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", + "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", + "dev": true + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -4370,6 +4986,12 @@ "dev": true, "requires": {} }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4391,6 +5013,12 @@ "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true }, + "untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", diff --git a/package.json b/package.json index 67d9cbb..865a693 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,8 @@ "@typescript-eslint/eslint-plugin": "^6.7.2", "@typescript-eslint/parser": "^6.7.2", "eslint": "^8.49.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0", "husky": "^8.0.3", "lint-staged": "^14.0.1", "prettier": "^3.0.3", diff --git a/src/components/camera.ts b/src/components/camera.ts new file mode 100644 index 0000000..5c65136 --- /dev/null +++ b/src/components/camera.ts @@ -0,0 +1,61 @@ +import * as THREE from 'three'; +import { MapControls } from 'three/addons/controls/MapControls.js'; +import { CameraState } from '../types'; + +export let camera: THREE.OrthographicCamera; +export let cameraControls: MapControls; + +const camX = -1; +const camY = 1; +const camZ = 1; +export const viewScale = 4; + +const cameraStates: CameraState[] = []; + +export function setupCamera() { + camera = new THREE.OrthographicCamera( + -window.innerWidth / viewScale, + window.innerWidth / viewScale, + window.innerHeight / viewScale, + -window.innerHeight / viewScale, + -1000, + 1000, + ); +} + +export function setupCameraControls(renderer: THREE.WebGLRenderer) { + cameraControls = new MapControls(camera, renderer.domElement); + cameraControls.enableDamping = false; + camera.position.set(camX, camY, camZ); + camera.lookAt(0, 0, 0); + cameraControls.update(); +} + +export function saveCameraState( + target: THREE.Vector3, + position: THREE.Vector3, + zoom: number, +) { + cameraStates.push({ + target, + position, + zoom, + }); + + return cameraStates.length; +} + +export function loadCameraState(index: number) { + if (index >= cameraStates.length) return; + const state = cameraStates[index]; + + cameraControls.target.copy(state.target); + camera.position.copy(state.position); + camera.zoom = state.zoom; + camera.updateProjectionMatrix(); + cameraControls.update(); +} + +export function getInitialCameraPosition() { + return new THREE.Vector3(camX, camY, camZ); +} diff --git a/src/components/gui.ts b/src/components/gui.ts index fd13582..7707353 100644 --- a/src/components/gui.ts +++ b/src/components/gui.ts @@ -1,14 +1,23 @@ import { GUI } from 'dat.gui'; -import { getMapObjects, loadCameraState } from '../setup'; +import { getMapObjects } from '../setup'; +import { loadCameraState } from './camera'; +import { hideLegend, showLegend, switchLegend } from './legend'; let gui: GUI; +const colourModeKeys = { + default: 'Full', + ext: 'Interior/Exterior', + nat: 'Natural/Artificial', +}; + const options = { visible: { labels: true, deprecatedPaths: true, - simpleMaterials: false, + legend: true, }, + colourMode: colourModeKeys.default, moveCameraIso: () => loadCameraState(0), moveCameraOverhead: () => loadCameraState(1), moveCameraSideEast: () => loadCameraState(2), @@ -17,6 +26,11 @@ const options = { export function setupGUI() { gui = new GUI(); + gui + .add(options, 'colourMode', Object.values(colourModeKeys)) + .name('Colour Mode') + .onChange(changeColourMode); + const showHideFolder = gui.addFolder('Show/Hide'); showHideFolder @@ -28,10 +42,9 @@ export function setupGUI() { .name('Deprecated Paths') .onChange(toggleDeprecatedPaths); showHideFolder - .add(options.visible, 'simpleMaterials') - .name('Simple Colours') - .onChange(toggleSimpleMaterials); - showHideFolder.open(); + .add(options.visible, 'legend') + .name('Legend') + .onChange(toggleLegend); const cameraFolder = gui.addFolder('Position Camera'); cameraFolder.add(options, 'moveCameraIso').name('Isometric'); @@ -64,12 +77,40 @@ function toggleDeprecatedPaths() { } } -function toggleSimpleMaterials() { +function changeColourMode() { const { pathObjects } = getMapObjects(); + let materialKey; + + switch (options.colourMode) { + case colourModeKeys.default: + materialKey = 'defaultMaterial'; + switchLegend(0); + break; + + case colourModeKeys.ext: + materialKey = 'extSimpleMaterial'; + switchLegend(1); + break; + + case colourModeKeys.nat: + materialKey = 'natSimpleMaterial'; + switchLegend(2); + break; + + default: + materialKey = 'defaultMaterial'; + switchLegend(0); + } for (const path of pathObjects) { - path.material = options.visible.simpleMaterials - ? path.userData.simpleMaterial - : path.userData.defaultMaterial; + path.material = path.userData[materialKey]; + } +} + +function toggleLegend() { + if (options.visible.legend) { + showLegend(); + } else { + hideLegend(); } } diff --git a/src/components/legend.ts b/src/components/legend.ts new file mode 100644 index 0000000..0632804 --- /dev/null +++ b/src/components/legend.ts @@ -0,0 +1,97 @@ +import { + DefaultPathPropertyDefinition, + DefaultPathPropertyDefinitions, + SimplePathPropertyDefinition, + SimplePathPropertyDefinitions, + defaultPathProps, + simplePathProps, +} from '../config/pathProps'; + +let legendContainer: HTMLElement | null; +const legends: HTMLElement[] = []; + +export function setupLegend() { + legendContainer = document.getElementById('legendContainer'); + if (legendContainer === null) return; + + const defaultLegendDiv = createLegend(defaultPathProps, 'full'); + legendContainer.appendChild(defaultLegendDiv); + legends.push(defaultLegendDiv); + + const extSimpleLegendDiv = createLegend( + { + simpleInterior: simplePathProps.simpleInterior, + simpleExterior: simplePathProps.simpleExterior, + }, + 'full', + ); + legendContainer.appendChild(extSimpleLegendDiv); + legends.push(extSimpleLegendDiv); + + const natSimpleLegendDiv = createLegend( + { + simpleNatural: simplePathProps.simpleNatural, + simpleArtificial: simplePathProps.simpleArtificial, + }, + 'full', + ); + legendContainer.appendChild(natSimpleLegendDiv); + legends.push(natSimpleLegendDiv); + + switchLegend(0); +} + +function createLegend( + propDefs: DefaultPathPropertyDefinitions | SimplePathPropertyDefinitions, + id: string, +) { + const legendDiv = document.createElement('div'); + legendDiv.id = id; + legendDiv.className = 'legend'; + const legendElemList = document.createElement('ul'); + legendDiv.appendChild(legendElemList); + + for (const propName in propDefs) { + const listElem = createLegendElement(propDefs[propName]); + legendElemList.appendChild(listElem); + } + + return legendDiv; +} + +function createLegendElement( + propDef: DefaultPathPropertyDefinition | SimplePathPropertyDefinition, +) { + const listElem = document.createElement('li'); + const swatch = document.createElement('span'); + const label = document.createElement('span'); + + listElem.className = 'legendElem'; + + swatch.className = 'legendSwatch'; + swatch.style.backgroundColor = `#${propDef.colour}`; + label.innerHTML = propDef.name; + + listElem.appendChild(swatch); + listElem.appendChild(label); + + return listElem; +} + +export function switchLegend(index: number) { + legends.forEach( + (legend, i) => (legend.style.display = index === i ? 'block' : 'none'), + ); +} + +export function showLegend() { + if (legendContainer) { + legendContainer.style.display = 'block'; + } +} + +export function hideLegend() { + if (legendContainer) { + legendContainer.style.display = 'none'; + } +} diff --git a/src/components/mapObjects.ts b/src/components/mapObjects.ts index 080c3c9..0b088aa 100644 --- a/src/components/mapObjects.ts +++ b/src/components/mapObjects.ts @@ -2,7 +2,9 @@ import * as THREE from 'three'; import { CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js'; import { LineGeometry } from 'three/addons/lines/LineGeometry.js'; import { Line2 } from 'three/addons/lines/Line2.js'; -import { getMaterial, simpleMaterialMap } from './materials'; +import { getMaterial } from './materials'; + +import { defaultPathProps } from '../config/pathProps'; import { type LineMaterial } from 'three/addons/lines/LineMaterial.js'; import { @@ -28,14 +30,23 @@ export function createPath(pathData: PathData, id: number) { if (rawPoints.length === 0) return null; const points = rawPoints.flat(1); + const defaultMaterial = ( deprecated ? getMaterial(`${type}Deprecated`) : getMaterial(type) ) as LineMaterial; - const simpleMaterial = ( - deprecated - ? getMaterial(`${simpleMaterialMap[type]}Deprecated`) - : getMaterial(simpleMaterialMap[type]) - ) as LineMaterial; + + const { isExterior, isNatural } = defaultPathProps[type]; + + const extSimpleMaterial = isExterior + ? getMaterial(`simpleExterior${deprecated ? 'Deprecated' : ''}`) + : (getMaterial( + `simpleInterior${deprecated ? 'Deprecated' : ''}`, + ) as LineMaterial); + const natSimpleMaterial = isNatural + ? getMaterial(`simpleNatural${deprecated ? 'Deprecated' : ''}`) + : (getMaterial( + `simpleArtificial${deprecated ? 'Deprecated' : ''}`, + ) as LineMaterial); const pathGeom = new LineGeometry().setPositions(points); const pathMesh = new Line2(pathGeom, defaultMaterial); @@ -47,7 +58,8 @@ export function createPath(pathData: PathData, id: number) { pathMesh.userData.type = type; pathMesh.userData.deprecated = deprecated; pathMesh.userData.defaultMaterial = defaultMaterial; - pathMesh.userData.simpleMaterial = simpleMaterial; + pathMesh.userData.extSimpleMaterial = extSimpleMaterial; + pathMesh.userData.natSimpleMaterial = natSimpleMaterial; return pathMesh; } diff --git a/src/components/materials.ts b/src/components/materials.ts index 7c76f58..b82c9b8 100644 --- a/src/components/materials.ts +++ b/src/components/materials.ts @@ -4,18 +4,6 @@ import materialDefs from '../config/materials'; const materials: Record = {}; -export const simpleMaterialMap = { - ugTunnel: 'ugTunnel', - ogTunnel: 'ugTunnel', - cBridge: 'ugTunnel', - oBridge: 'ugTunnel', - exPath: 'exPath', - nCave: 'exPath', - ladder: 'ugTunnel', - bastion: 'exPath', - nFortress: 'exPath', -}; - export function initMaterials() { const { mesh, line } = materialDefs; diff --git a/src/components/stats.ts b/src/components/stats.ts index 27ac64e..f1919d1 100644 --- a/src/components/stats.ts +++ b/src/components/stats.ts @@ -1,10 +1,9 @@ import Stats from 'three/addons/libs/stats.module.js'; -import { getSceneObjects } from '../setup'; +import { renderer } from '../setup'; let statsPanel: Stats; let trisPanel: Stats.Panel; let drawsPanel: Stats.Panel; -let renderer: THREE.WebGLRenderer; export function addStatsPanel() { statsPanel = new Stats(); @@ -16,8 +15,6 @@ export function addStatsPanel() { document.body.appendChild(statsPanel.dom); statsPanel.showPanel(0); - - renderer = getSceneObjects().renderer; } export function updateStatsPanel() { diff --git a/src/config/materials.ts b/src/config/materials.ts index b2aead6..ef2f7e5 100644 --- a/src/config/materials.ts +++ b/src/config/materials.ts @@ -1,9 +1,13 @@ import * as THREE from 'three'; import { LineMaterialParameters } from 'three/addons/lines/LineMaterial.js'; +import { defaultPathProps, simplePathProps } from './pathProps'; + +type LineMaterialDefinitions = Record; + export type MaterialDefinitions = { mesh: Record; - line: Record; + line: LineMaterialDefinitions; }; const materials: MaterialDefinitions = { @@ -28,16 +32,23 @@ const materials: MaterialDefinitions = { side: THREE.DoubleSide, }, }, - line: { - ogTunnel: { color: 0x8090ff, linewidth: 0.0025 }, // Overground Tunnel - ugTunnel: { color: 0x80b0d0, linewidth: 0.0025 }, // Underground Tunnel - cBridge: { color: 0x80ff80, linewidth: 0.0025 }, // Covered Bridge - oBridge: { color: 0xffd040, linewidth: 0.0025 }, // Open Bridge - exPath: { color: 0xc00000, linewidth: 0.0025 }, // External Path - nCave: { color: 0xc06000, linewidth: 0.0025 }, // Natural Cave - ladder: { color: 0xffffff, linewidth: 0.0025 }, // Ladder - bastion: { color: 0x404080, linewidth: 0.0025 }, // Bastion - }, + line: (() => { + const lineMaterials: LineMaterialDefinitions = {}; + + // Parse default path palette + for (const pathKey in defaultPathProps) { + const colourInt = parseInt(defaultPathProps[pathKey].colour, 16); + lineMaterials[pathKey] = { color: colourInt, linewidth: 0.0025 }; + } + + // Parse simple path palette + for (const pathKey in simplePathProps) { + const colourInt = parseInt(simplePathProps[pathKey].colour, 16); + lineMaterials[pathKey] = { color: colourInt, linewidth: 0.0025 }; + } + + return lineMaterials; + })(), }; export default materials; diff --git a/src/config/pathProps.ts b/src/config/pathProps.ts new file mode 100644 index 0000000..61f8439 --- /dev/null +++ b/src/config/pathProps.ts @@ -0,0 +1,90 @@ +export type DefaultPathPropertyDefinition = { + name: string; + colour: string; + isExterior: boolean; + isNatural: boolean; +}; + +export type SimplePathPropertyDefinition = { + name: string; + colour: string; +}; + +export type DefaultPathPropertyDefinitions = Record< + string, + DefaultPathPropertyDefinition +>; +export type SimplePathPropertyDefinitions = Record< + string, + SimplePathPropertyDefinition +>; + +export const defaultPathProps: DefaultPathPropertyDefinitions = { + ogTunnel: { + name: 'Surface Tunnel', + colour: '8090ff', + isExterior: false, + isNatural: false, + }, + ugTunnel: { + name: 'Underground Tunnel', + colour: '80b0d0', + isExterior: false, + isNatural: false, + }, + cBridge: { + name: 'Covered Bridge', + colour: '80ff80', + isExterior: false, + isNatural: false, + }, + oBridge: { + name: 'Open Bridge', + colour: 'ffd040', + isExterior: true, + isNatural: false, + }, + exPath: { + name: 'Open Path', + colour: 'c00000', + isExterior: true, + isNatural: true, + }, + nCave: { + name: 'Cave', + colour: 'c06000', + isExterior: false, + isNatural: true, + }, + ladder: { + name: 'Ladder', + colour: 'ffffff', + isExterior: true, + isNatural: false, + }, + bastion: { + name: 'Bastion', + colour: '404080', + isExterior: true, + isNatural: true, + }, +}; + +export const simplePathProps: SimplePathPropertyDefinitions = { + simpleInterior: { + name: 'Interior', + colour: '80b0d0', + }, + simpleExterior: { + name: 'Exterior', + colour: 'c00000', + }, + simpleNatural: { + name: 'Natural', + colour: 'c06000', + }, + simpleArtificial: { + name: 'Artificial', + colour: '80ff80', + }, +}; diff --git a/src/data/paths.ts b/src/data/paths.ts index 764ca0a..2e54aad 100644 --- a/src/data/paths.ts +++ b/src/data/paths.ts @@ -1032,7 +1032,7 @@ const data: PathData[] = [ ], }, { - type: 'nCave', + type: 'exPath', visible: true, deprecated: false, points: [ @@ -1047,6 +1047,14 @@ const data: PathData[] = [ [599, 100, 9], [609, 100, 17], [615, 100, 20.5], + ], + }, + { + type: 'nCave', + visible: true, + deprecated: false, + points: [ + [615, 100, 20.5], [622, 100, 20.5], [623, 101, 20.5], [634, 101, 21.5], @@ -1063,7 +1071,7 @@ const data: PathData[] = [ ], }, { - type: 'nCave', + type: 'exPath', visible: true, deprecated: false, points: [ @@ -1080,6 +1088,14 @@ const data: PathData[] = [ [576.5, 101, -71], [576.5, 102, -72], [576.5, 102, -80], + ], + }, + { + type: 'nCave', + visible: true, + deprecated: false, + points: [ + [576.5, 102, -80], [576.5, 103, -81], [576.5, 103, -85], [576.5, 100, -88], @@ -1089,7 +1105,7 @@ const data: PathData[] = [ ], }, { - type: 'nCave', + type: 'exPath', visible: true, deprecated: false, points: [ diff --git a/src/data/portals.ts b/src/data/portals.ts index 889f14a..aaa9d0a 100644 --- a/src/data/portals.ts +++ b/src/data/portals.ts @@ -158,7 +158,7 @@ const data: PortalData[] = [ location: [-255.5, 35, 169], }, { - label: 'Grand Sinkhole', + label: 'Grand Sinkhole Cave', location: [-255.5, 34, 197], }, ]; diff --git a/src/setup.ts b/src/setup.ts index 2c99b5e..5d3d097 100644 --- a/src/setup.ts +++ b/src/setup.ts @@ -1,6 +1,14 @@ import * as THREE from 'three'; -import { MapControls } from 'three/addons/controls/MapControls.js'; import { CSS2DRenderer } from 'three/addons/renderers/CSS2DRenderer.js'; +import { + camera, + cameraControls, + getInitialCameraPosition, + saveCameraState, + setupCamera, + setupCameraControls, + viewScale, +} from './components/camera'; import { createDoor, createPath, @@ -11,7 +19,7 @@ import { getMaterial, initMaterials } from './components/materials'; import { addStatsPanel, updateStatsPanel } from './components/stats'; import { type CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js'; -import { CameraState, DoorData, PathData, PortalData, RoomData } from './types'; +import { DoorData, PathData, PortalData, RoomData } from './types'; import featureConfig from './config/features.json'; @@ -20,22 +28,14 @@ import roomsData from './data/rooms'; import doorsData from './data/doors'; import portalsData from './data/portals'; import { setupGUI } from './components/gui'; - -const camX = -1; -const camY = 1; -const camZ = 1; -const viewScale = 4; +import { setupLegend } from './components/legend'; let scene: THREE.Scene; -let camera: THREE.OrthographicCamera; -let cameraControls: MapControls; -let renderer: THREE.WebGLRenderer; +export let renderer: THREE.WebGLRenderer; let labelRenderer: CSS2DRenderer; let raycaster: THREE.Raycaster; let pointer: THREE.Vector2; -const cameraStates: CameraState[] = []; - const roomObjects: THREE.Mesh[] = []; const doorObjects: THREE.Mesh[] = []; const portalObjects: THREE.Mesh[] = []; @@ -45,14 +45,7 @@ const labelObjects: CSS2DObject[] = []; export function setupScene() { // Set up scene, camera, raycaster scene = new THREE.Scene(); - camera = new THREE.OrthographicCamera( - -window.innerWidth / viewScale, - window.innerWidth / viewScale, - window.innerHeight / viewScale, - -window.innerHeight / viewScale, - -1000, - 1000, - ); + setupCamera(); raycaster = new THREE.Raycaster(); pointer = new THREE.Vector2(); @@ -67,24 +60,22 @@ export function setupScene() { labelRenderer.domElement.className = 'labelRenderer'; document.body.appendChild(labelRenderer.domElement); + setupLegend(); + // Add Stats panel if (import.meta.env.DEV) { addStatsPanel(); } - // Set up camera controls - cameraControls = new MapControls(camera, renderer.domElement); - cameraControls.enableDamping = false; - camera.position.set(camX, camY, camZ); - camera.lookAt(0, 0, 0); - cameraControls.update(); + setupCameraControls(renderer); + const initCameraPos = getInitialCameraPosition(); + saveCameraState(new THREE.Vector3(0, 0, 0), initCameraPos, 1); // Isometric Camera saveCameraState( new THREE.Vector3(0, 0, 0), - new THREE.Vector3(camX, camY, camZ), + new THREE.Vector3(0, initCameraPos.y, 0), 1, - ); // Isometric Camera - saveCameraState(new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, camY, 0), 1); // Overhead Camera + ); // Overhead Camera saveCameraState(new THREE.Vector3(0, 64, 0), new THREE.Vector3(-1, 64, 0), 1); // Side Camera (East) saveCameraState(new THREE.Vector3(0, 64, 0), new THREE.Vector3(0, 64, 1), 1); // Side Camera (North) @@ -163,47 +154,6 @@ export function setupScene() { setupGUI(); } -export function getMapObjects() { - return { - roomObjects, - pathObjects, - doorObjects, - portalObjects, - labelObjects, - }; -} - -export function getSceneObjects() { - return { - renderer, - }; -} - -function saveCameraState( - target: THREE.Vector3, - position: THREE.Vector3, - zoom: number, -) { - cameraStates.push({ - target, - position, - zoom, - }); - - return cameraStates.length; -} - -export function loadCameraState(index: number) { - if (index >= cameraStates.length) return; - const state = cameraStates[index]; - - cameraControls.target.copy(state.target); - camera.position.copy(state.position); - camera.zoom = state.zoom; - camera.updateProjectionMatrix(); - cameraControls.update(); -} - function initMapObjects( data: T[], createObjFunc: (object: T, id: number) => boolean, @@ -216,6 +166,16 @@ function initMapObjects( } } +export function getMapObjects() { + return { + roomObjects, + pathObjects, + doorObjects, + portalObjects, + labelObjects, + }; +} + function onPointerDown(event: MouseEvent) { pointer.x = (event.clientX / window.innerWidth) * 2 - 1; pointer.y = (event.clientY / window.innerHeight) * 2 - 1; diff --git a/src/style.css b/src/style.css index 4b9f1a0..a2b81f3 100644 --- a/src/style.css +++ b/src/style.css @@ -16,6 +16,40 @@ body { pointer-events: none; } +#legendContainer { + z-index: 10000; + position: absolute; + left: 0; + bottom: 0; + padding: 0.5rem; + min-width: 1rem; + min-height: 1rem; + background-color: #101010; + pointer-events: none; +} + +.legend ul { + list-style: none; + margin: 0; + padding: 0; + color: white; + font-family: sans-serif; + font-size: 0.75rem; +} + +.legendElem { + display: flex; + align-items: center; +} + +.legendSwatch { + display: inline-flex; + padding: 0.5rem; + margin: 0.25rem; + max-width: 0.5rem; + max-height: 0.5rem; +} + /* Make sure dat.gui stays on top of CSS2DRenderer labels */ .dg.ac { z-index: 10000 !important;