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;