From 9e4ba2187520f71c5a9b0c9751807cdc4c0368df Mon Sep 17 00:00:00 2001 From: Domenico Mancini Date: Wed, 26 Mar 2025 16:38:47 +0100 Subject: [PATCH 01/10] chore: add dependencies for 3d viewer --- dash/package.json | 4 + dash/yarn.lock | 602 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 606 insertions(+) diff --git a/dash/package.json b/dash/package.json index eb07336a..cb5c30a2 100644 --- a/dash/package.json +++ b/dash/package.json @@ -12,6 +12,9 @@ "dependencies": { "@fireworks-js/react": "^2.10.8", "@headlessui/react": "2.2.0", + "@react-three/drei": "^10.0.5", + "@react-three/fiber": "^9.1.0", + "@types/three": "^0.174.0", "clsx": "2.1.1", "framer-motion": "11.11.17", "geist": "1.3.1", @@ -21,6 +24,7 @@ "react": "18.3.1", "react-dom": "18.3.1", "sharp": "0.33.5", + "three": "^0.174.0", "zod": "3.23.8", "zustand": "5.0.1" }, diff --git a/dash/yarn.lock b/dash/yarn.lock index 19aa075b..dc6c3561 100644 --- a/dash/yarn.lock +++ b/dash/yarn.lock @@ -12,6 +12,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.26.0": + version: 7.26.10 + resolution: "@babel/runtime@npm:7.26.10" + dependencies: + regenerator-runtime: "npm:^0.14.0" + checksum: 10c0/6dc6d88c7908f505c4f7770fb4677dfa61f68f659b943c2be1f2a99cb6680343462867abf2d49822adc435932919b36c77ac60125793e719ea8745f2073d3745 + languageName: node + linkType: hard + "@emnapi/runtime@npm:^1.2.0": version: 1.3.1 resolution: "@emnapi/runtime@npm:1.3.1" @@ -331,6 +340,24 @@ __metadata: languageName: node linkType: hard +"@mediapipe/tasks-vision@npm:0.10.17": + version: 0.10.17 + resolution: "@mediapipe/tasks-vision@npm:0.10.17" + checksum: 10c0/f2f5dd9ca39d562b902a6d964f8b786f17a143f42776d0d26b8c79632b10cf210e28d98ec92de757b7bb2e6595ed4f9bbf5e49f1709b09dc08709bd276ced442 + languageName: node + linkType: hard + +"@monogrid/gainmap-js@npm:^3.0.6": + version: 3.1.0 + resolution: "@monogrid/gainmap-js@npm:3.1.0" + dependencies: + promise-worker-transferable: "npm:^1.0.4" + peerDependencies: + three: ">= 0.159.0" + checksum: 10c0/0afae39fdca31456a57803d5751c0e5b742d8dfac53aa95b21b43fbf02ad250cd80f5ae0368071128729754b89ff1be6317e1e23be86706c0d3e227bb5f82db2 + languageName: node + linkType: hard + "@next/env@npm:15.0.3": version: 15.0.3 resolution: "@next/env@npm:15.0.3" @@ -516,6 +543,85 @@ __metadata: languageName: node linkType: hard +"@react-three/drei@npm:^10.0.5": + version: 10.0.5 + resolution: "@react-three/drei@npm:10.0.5" + dependencies: + "@babel/runtime": "npm:^7.26.0" + "@mediapipe/tasks-vision": "npm:0.10.17" + "@monogrid/gainmap-js": "npm:^3.0.6" + "@use-gesture/react": "npm:^10.3.1" + camera-controls: "npm:^2.9.0" + cross-env: "npm:^7.0.3" + detect-gpu: "npm:^5.0.56" + glsl-noise: "npm:^0.0.0" + hls.js: "npm:^1.5.17" + maath: "npm:^0.10.8" + meshline: "npm:^3.3.1" + stats-gl: "npm:^2.2.8" + stats.js: "npm:^0.17.0" + suspend-react: "npm:^0.1.3" + three-mesh-bvh: "npm:^0.8.3" + three-stdlib: "npm:^2.35.6" + troika-three-text: "npm:^0.52.0" + tunnel-rat: "npm:^0.1.2" + use-sync-external-store: "npm:^1.4.0" + utility-types: "npm:^3.11.0" + zustand: "npm:^5.0.1" + peerDependencies: + "@react-three/fiber": ^9.0.0 + react: ^19 + react-dom: ^19 + three: ">=0.159" + peerDependenciesMeta: + react-dom: + optional: true + checksum: 10c0/9cddb291a5032563a382cfa31b66fd226158e0f98642c4a0f31d29a702a8c31e09857cc11542a201ff0fb86b7d5504ddfeae4bd83574258b3aebb7ea655e93e4 + languageName: node + linkType: hard + +"@react-three/fiber@npm:^9.1.0": + version: 9.1.0 + resolution: "@react-three/fiber@npm:9.1.0" + dependencies: + "@babel/runtime": "npm:^7.17.8" + "@types/react-reconciler": "npm:^0.28.9" + "@types/webxr": "npm:*" + base64-js: "npm:^1.5.1" + buffer: "npm:^6.0.3" + its-fine: "npm:^2.0.0" + react-reconciler: "npm:^0.31.0" + react-use-measure: "npm:^2.1.7" + scheduler: "npm:^0.25.0" + suspend-react: "npm:^0.1.3" + use-sync-external-store: "npm:^1.4.0" + zustand: "npm:^5.0.3" + peerDependencies: + expo: ">=43.0" + expo-asset: ">=8.4" + expo-file-system: ">=11.0" + expo-gl: ">=11.0" + react: ^19.0.0 + react-dom: ^19.0.0 + react-native: ">=0.78" + three: ">=0.156" + peerDependenciesMeta: + expo: + optional: true + expo-asset: + optional: true + expo-file-system: + optional: true + expo-gl: + optional: true + react-dom: + optional: true + react-native: + optional: true + checksum: 10c0/418103e098d929fcee8b7f05e14f976d01b453958e23f79b00f2d477c9309092e0696de88182155106c6a039f1248112890dcfcbbafb8f2dc79830a101a164fb + languageName: node + linkType: hard + "@react-types/shared@npm:^3.25.0": version: 3.25.0 resolution: "@react-types/shared@npm:3.25.0" @@ -569,6 +675,20 @@ __metadata: languageName: node linkType: hard +"@tweenjs/tween.js@npm:~23.1.3": + version: 23.1.3 + resolution: "@tweenjs/tween.js@npm:23.1.3" + checksum: 10c0/811b30f5f0e7409fb41833401c501c2d6f600eb5f43039dd9067a7f70aff6dad5f5ce1233848e13f0b33a269a160d9c133f344d986cbff4f1f6b72ddecd06c89 + languageName: node + linkType: hard + +"@types/draco3d@npm:^1.4.0": + version: 1.4.10 + resolution: "@types/draco3d@npm:1.4.10" + checksum: 10c0/431e333b2fd67e2b081e8697e71dfb82a125fc04b2cbfa0205e4d521719d3749c964b2bf82bb7944ba0a12e5bc3d9afe387a58e68b195d838fcdbc65c8572a35 + languageName: node + linkType: hard + "@types/lodash@npm:4.17.13": version: 4.17.13 resolution: "@types/lodash@npm:4.17.13" @@ -585,6 +705,13 @@ __metadata: languageName: node linkType: hard +"@types/offscreencanvas@npm:^2019.6.4": + version: 2019.7.3 + resolution: "@types/offscreencanvas@npm:2019.7.3" + checksum: 10c0/6d1dfae721d321cd2b5435f347a0e53b09f33b2f9e9333396480f592823bc323847b8169f7d251d2285cb93dbc1ba2e30741ac5cf4b1c003d660fd4c24526963 + languageName: node + linkType: hard + "@types/pako@npm:2.0.3": version: 2.0.3 resolution: "@types/pako@npm:2.0.3" @@ -608,6 +735,15 @@ __metadata: languageName: node linkType: hard +"@types/react-reconciler@npm:^0.28.9": + version: 0.28.9 + resolution: "@types/react-reconciler@npm:0.28.9" + peerDependencies: + "@types/react": "*" + checksum: 10c0/9fe71fa856aab2cd4742fe0416bdd4f5c82ecc05ef6451ee7fcb65a5efdf5fa588f5820fbe2a665b15371b0da3bfc4097f28bb6d450b9a834af2d0fc00f403bd + languageName: node + linkType: hard + "@types/react@npm:*, @types/react@npm:18.3.12": version: 18.3.12 resolution: "@types/react@npm:18.3.12" @@ -618,6 +754,59 @@ __metadata: languageName: node linkType: hard +"@types/stats.js@npm:*": + version: 0.17.3 + resolution: "@types/stats.js@npm:0.17.3" + checksum: 10c0/ccccc992c6dfe08fb85049aa3ce44ca7e428db8da4a3edd20298f1c8b72768021fa8bacdfbe8e9735a7552ee5d57f667c6f557050ad2d9a87b699b3566a6177a + languageName: node + linkType: hard + +"@types/three@npm:*, @types/three@npm:^0.174.0": + version: 0.174.0 + resolution: "@types/three@npm:0.174.0" + dependencies: + "@tweenjs/tween.js": "npm:~23.1.3" + "@types/stats.js": "npm:*" + "@types/webxr": "npm:*" + "@webgpu/types": "npm:*" + fflate: "npm:~0.8.2" + meshoptimizer: "npm:~0.18.1" + checksum: 10c0/338e9d8d01d373014ee3c3b686a0d633c1a29603fe199c7c5340fc1a35c17e6eea0d0a7bba5ff9a33b8e462e5b471c0829c2aaa34ab2300e8d6e7e3f885e45b5 + languageName: node + linkType: hard + +"@types/webxr@npm:*, @types/webxr@npm:^0.5.2": + version: 0.5.21 + resolution: "@types/webxr@npm:0.5.21" + checksum: 10c0/7b6a7001f8592a0c8f1bff46352a451a5bcc24d016d10985a4d7bff9b778c4530fb82e48db1a7e1da67ff4f75bff49384fdac5e56fa103455f8281c3c0f403a6 + languageName: node + linkType: hard + +"@use-gesture/core@npm:10.3.1": + version: 10.3.1 + resolution: "@use-gesture/core@npm:10.3.1" + checksum: 10c0/2e3b5c0f7fe26cdb47be3a9c2a58a6a9edafc5b2895b07d2898eda9ab5a2b29fb0098b15597baa0856907b593075cd44cc69bba4785c9cfb7b6fabaa3b52cd3e + languageName: node + linkType: hard + +"@use-gesture/react@npm:^10.3.1": + version: 10.3.1 + resolution: "@use-gesture/react@npm:10.3.1" + dependencies: + "@use-gesture/core": "npm:10.3.1" + peerDependencies: + react: ">= 16.8.0" + checksum: 10c0/978da66e4e7c424866ad52eba8fdf0ce93a4c8fc44f8837c7043e68c6a6107cd67e817fffb27f7db2ae871ef2f6addb0c8ddf1586f24c67b7e6aef1646c668cf + languageName: node + linkType: hard + +"@webgpu/types@npm:*": + version: 0.1.58 + resolution: "@webgpu/types@npm:0.1.58" + checksum: 10c0/6a2f5acfc7aa9e2c47f843897c3df31d79b099cdbc0779ddac9bf7bd126675d5344329a6824daf3a88cac90a8e058c342aa2813efe781040aba7b4fb356d7f4d + languageName: node + linkType: hard + "abbrev@npm:^2.0.0": version: 2.0.0 resolution: "abbrev@npm:2.0.0" @@ -723,6 +912,22 @@ __metadata: languageName: node linkType: hard +"base64-js@npm:^1.3.1, base64-js@npm:^1.5.1": + version: 1.5.1 + resolution: "base64-js@npm:1.5.1" + checksum: 10c0/f23823513b63173a001030fae4f2dabe283b99a9d324ade3ad3d148e218134676f1ee8568c877cd79ec1c53158dcf2d2ba527a97c606618928ba99dd930102bf + languageName: node + linkType: hard + +"bidi-js@npm:^1.0.2": + version: 1.0.3 + resolution: "bidi-js@npm:1.0.3" + dependencies: + require-from-string: "npm:^2.0.2" + checksum: 10c0/fdddea4aa4120a34285486f2267526cd9298b6e8b773ad25e765d4f104b6d7437ab4ba542e6939e3ac834a7570bcf121ee2cf6d3ae7cd7082c4b5bedc8f271e1 + languageName: node + linkType: hard + "binary-extensions@npm:^2.0.0": version: 2.3.0 resolution: "binary-extensions@npm:2.3.0" @@ -762,6 +967,16 @@ __metadata: languageName: node linkType: hard +"buffer@npm:^6.0.3": + version: 6.0.3 + resolution: "buffer@npm:6.0.3" + dependencies: + base64-js: "npm:^1.3.1" + ieee754: "npm:^1.2.1" + checksum: 10c0/2a905fbbcde73cc5d8bd18d1caa23715d5f83a5935867c2329f0ac06104204ba7947be098fe1317fbd8830e26090ff8e764f08cd14fefc977bb248c3487bcbd0 + languageName: node + linkType: hard + "busboy@npm:1.6.0": version: 1.6.0 resolution: "busboy@npm:1.6.0" @@ -798,6 +1013,15 @@ __metadata: languageName: node linkType: hard +"camera-controls@npm:^2.9.0": + version: 2.10.0 + resolution: "camera-controls@npm:2.10.0" + peerDependencies: + three: ">=0.126.1" + checksum: 10c0/c4289f632f536771cf0fc8b28c3208f6c11e6bcaec2b85319e176a2d85c7e4f83a369d2b131306bd4a79df986bedb1f5e3d50a198803e91dc0e4e9ed81c59d0c + languageName: node + linkType: hard + "caniuse-lite@npm:^1.0.30001579, caniuse-lite@npm:^1.0.30001646, caniuse-lite@npm:^1.0.30001669": version: 1.0.30001680 resolution: "caniuse-lite@npm:1.0.30001680" @@ -895,6 +1119,18 @@ __metadata: languageName: node linkType: hard +"cross-env@npm:^7.0.3": + version: 7.0.3 + resolution: "cross-env@npm:7.0.3" + dependencies: + cross-spawn: "npm:^7.0.1" + bin: + cross-env: src/bin/cross-env.js + cross-env-shell: src/bin/cross-env-shell.js + checksum: 10c0/f3765c25746c69fcca369655c442c6c886e54ccf3ab8c16847d5ad0e91e2f337d36eedc6599c1227904bf2a228d721e690324446876115bc8e7b32a866735ecf + languageName: node + linkType: hard + "cross-spawn@npm:^7.0.0": version: 7.0.5 resolution: "cross-spawn@npm:7.0.5" @@ -906,6 +1142,17 @@ __metadata: languageName: node linkType: hard +"cross-spawn@npm:^7.0.1": + version: 7.0.6 + resolution: "cross-spawn@npm:7.0.6" + dependencies: + path-key: "npm:^3.1.0" + shebang-command: "npm:^2.0.0" + which: "npm:^2.0.1" + checksum: 10c0/053ea8b2135caff68a9e81470e845613e374e7309a47731e81639de3eaeb90c3d01af0e0b44d2ab9d50b43467223b88567dfeb3262db942dc063b9976718ffc1 + languageName: node + linkType: hard + "cssesc@npm:^3.0.0": version: 3.0.0 resolution: "cssesc@npm:3.0.0" @@ -934,6 +1181,15 @@ __metadata: languageName: node linkType: hard +"detect-gpu@npm:^5.0.56": + version: 5.0.70 + resolution: "detect-gpu@npm:5.0.70" + dependencies: + webgl-constants: "npm:^1.1.1" + checksum: 10c0/5a2053d4779edb3490821599b316b5362c8b55c30b82806c64bcfd21f40c67e2debfd677937899080aea2b27765471552bafeeae9c9f1cf63bdd07461a7e2163 + languageName: node + linkType: hard + "detect-libc@npm:^2.0.3": version: 2.0.3 resolution: "detect-libc@npm:2.0.3" @@ -955,6 +1211,13 @@ __metadata: languageName: node linkType: hard +"draco3d@npm:^1.4.1": + version: 1.5.7 + resolution: "draco3d@npm:1.5.7" + checksum: 10c0/4419509bb93c31560a22a1a54e1c394a5b5017cab39941120c75151d941c11dec05925abf31f597ac2694c570b78c04f82aa3d69e5311f8f8e71fc8b9d92d12f + languageName: node + linkType: hard + "eastasianwidth@npm:^0.2.0": version: 0.2.0 resolution: "eastasianwidth@npm:0.2.0" @@ -1026,11 +1289,14 @@ __metadata: dependencies: "@fireworks-js/react": "npm:^2.10.8" "@headlessui/react": "npm:2.2.0" + "@react-three/drei": "npm:^10.0.5" + "@react-three/fiber": "npm:^9.1.0" "@types/lodash": "npm:4.17.13" "@types/node": "npm:20.11.10" "@types/pako": "npm:2.0.3" "@types/react": "npm:18.3.12" "@types/react-dom": "npm:18.3.1" + "@types/three": "npm:^0.174.0" autoprefixer: "npm:10.4.20" clsx: "npm:2.1.1" framer-motion: "npm:11.11.17" @@ -1045,6 +1311,7 @@ __metadata: react-dom: "npm:18.3.1" sharp: "npm:0.33.5" tailwindcss: "npm:3.4.15" + three: "npm:^0.174.0" typescript: "npm:5.6.3" zod: "npm:3.23.8" zustand: "npm:5.0.1" @@ -1073,6 +1340,20 @@ __metadata: languageName: node linkType: hard +"fflate@npm:^0.6.9": + version: 0.6.10 + resolution: "fflate@npm:0.6.10" + checksum: 10c0/c452f720e4cb404b300fceb8ef0bdf345f18fbfd3f57f7d0974dce5f5e2ac0e8dd4b6ff4bb7061ae74fd919b9c707172f9dbd44d91149b1137199b8c705768f1 + languageName: node + linkType: hard + +"fflate@npm:~0.8.2": + version: 0.8.2 + resolution: "fflate@npm:0.8.2" + checksum: 10c0/03448d630c0a583abea594835a9fdb2aaf7d67787055a761515bf4ed862913cfd693b4c4ffd5c3f3b355a70cf1e19033e9ae5aedcca103188aaff91b8bd6e293 + languageName: node + linkType: hard + "fill-range@npm:^7.1.1": version: 7.1.1 resolution: "fill-range@npm:7.1.1" @@ -1213,6 +1494,13 @@ __metadata: languageName: node linkType: hard +"glsl-noise@npm:^0.0.0": + version: 0.0.0 + resolution: "glsl-noise@npm:0.0.0" + checksum: 10c0/442630e86ed109079f4ed92f3b59d24d0b9da60c03cb8ac9313a056996bf4b5d696b4c6121ee29952e8a19c5cf174a999cfe672464959db36fc75334c3134677 + languageName: node + linkType: hard + "graceful-fs@npm:^4.2.6": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" @@ -1229,6 +1517,13 @@ __metadata: languageName: node linkType: hard +"hls.js@npm:^1.5.17": + version: 1.5.20 + resolution: "hls.js@npm:1.5.20" + checksum: 10c0/1d0357e381f6fa81a6b2737aab7fcf88b79b8f9b23c66665e0405eea6c2cfb58df00ba7fc72ee71990c6898d67f092732f8e7046914098ebba2c33323dc627f4 + languageName: node + linkType: hard + "http-cache-semantics@npm:^4.1.1": version: 4.1.1 resolution: "http-cache-semantics@npm:4.1.1" @@ -1265,6 +1560,20 @@ __metadata: languageName: node linkType: hard +"ieee754@npm:^1.2.1": + version: 1.2.1 + resolution: "ieee754@npm:1.2.1" + checksum: 10c0/b0782ef5e0935b9f12883a2e2aa37baa75da6e66ce6515c168697b42160807d9330de9a32ec1ed73149aea02e0d822e572bca6f1e22bdcbd2149e13b050b17bb + languageName: node + linkType: hard + +"immediate@npm:~3.0.5": + version: 3.0.6 + resolution: "immediate@npm:3.0.6" + checksum: 10c0/f8ba7ede69bee9260241ad078d2d535848745ff5f6995c7c7cb41cfdc9ccc213f66e10fa5afb881f90298b24a3f7344b637b592beb4f54e582770cdce3f1f039 + languageName: node + linkType: hard + "imurmurhash@npm:^0.1.4": version: 0.1.4 resolution: "imurmurhash@npm:0.1.4" @@ -1351,6 +1660,13 @@ __metadata: languageName: node linkType: hard +"is-promise@npm:^2.1.0": + version: 2.2.2 + resolution: "is-promise@npm:2.2.2" + checksum: 10c0/2dba959812380e45b3df0fb12e7cb4d4528c989c7abb03ececb1d1fd6ab1cbfee956ca9daa587b9db1d8ac3c1e5738cf217bdb3dfd99df8c691be4c00ae09069 + languageName: node + linkType: hard + "isexe@npm:^2.0.0": version: 2.0.0 resolution: "isexe@npm:2.0.0" @@ -1365,6 +1681,17 @@ __metadata: languageName: node linkType: hard +"its-fine@npm:^2.0.0": + version: 2.0.0 + resolution: "its-fine@npm:2.0.0" + dependencies: + "@types/react-reconciler": "npm:^0.28.9" + peerDependencies: + react: ^19.0.0 + checksum: 10c0/1ff1ff3257c0c7eb115c9c26cf0506eb84162edc1a63d3136780d146f7c7833298b240d0fcb46888909773f1a7d16539e0c03f2482cff1a5a502d6436686fe21 + languageName: node + linkType: hard + "jackspeak@npm:^3.1.2": version: 3.4.3 resolution: "jackspeak@npm:3.4.3" @@ -1401,6 +1728,15 @@ __metadata: languageName: node linkType: hard +"lie@npm:^3.0.2": + version: 3.3.0 + resolution: "lie@npm:3.3.0" + dependencies: + immediate: "npm:~3.0.5" + checksum: 10c0/56dd113091978f82f9dc5081769c6f3b947852ecf9feccaf83e14a123bc630c2301439ce6182521e5fbafbde88e88ac38314327a4e0493a1bea7e0699a7af808 + languageName: node + linkType: hard + "lilconfig@npm:^2.1.0": version: 2.1.0 resolution: "lilconfig@npm:2.1.0" @@ -1440,6 +1776,16 @@ __metadata: languageName: node linkType: hard +"maath@npm:^0.10.8": + version: 0.10.8 + resolution: "maath@npm:0.10.8" + peerDependencies: + "@types/three": ">=0.134.0" + three: ">=0.134.0" + checksum: 10c0/fb61faac89567595f7e25f261f8a9750d0ced674fd16c77c6e486a0c4a00c572fa37969d613d017380f8ea4e8ff357d481dcd94240a104a7969bc7e95b9fe99e + languageName: node + linkType: hard + "make-fetch-happen@npm:^13.0.0": version: 13.0.1 resolution: "make-fetch-happen@npm:13.0.1" @@ -1467,6 +1813,22 @@ __metadata: languageName: node linkType: hard +"meshline@npm:^3.3.1": + version: 3.3.1 + resolution: "meshline@npm:3.3.1" + peerDependencies: + three: ">=0.137" + checksum: 10c0/95d5c7c90aca350530f37f9ea5c5e333a66ea0c50416fa6372c24a23321837a4c70d92d6a9a5942ee124484855a2d3fc8ca0656305feb8d8f86aa10513e324e4 + languageName: node + linkType: hard + +"meshoptimizer@npm:~0.18.1": + version: 0.18.1 + resolution: "meshoptimizer@npm:0.18.1" + checksum: 10c0/8a825c58b20b65585e8d00788843929b60c66ba4297e89afaa49f7c51ab9a0f7b9130f90cc9ad1b9b48b3d1bee3beb1bc93608acba0d73e78995c3e6e5ca436b + languageName: node + linkType: hard + "micromatch@npm:^4.0.4, micromatch@npm:^4.0.8": version: 4.0.8 resolution: "micromatch@npm:4.0.8" @@ -1914,6 +2276,13 @@ __metadata: languageName: node linkType: hard +"potpack@npm:^1.0.1": + version: 1.0.2 + resolution: "potpack@npm:1.0.2" + checksum: 10c0/670c23898a4257130858b960c2e654d3327c0f6a7e7091ff5846f213e65af8f9476320b995b8ad561a47a4d1c359c7ef347de57d22e7b02597051abb52bc85c4 + languageName: node + linkType: hard + "prettier-plugin-tailwindcss@npm:0.6.8": version: 0.6.8 resolution: "prettier-plugin-tailwindcss@npm:0.6.8" @@ -1998,6 +2367,16 @@ __metadata: languageName: node linkType: hard +"promise-worker-transferable@npm:^1.0.4": + version: 1.0.4 + resolution: "promise-worker-transferable@npm:1.0.4" + dependencies: + is-promise: "npm:^2.1.0" + lie: "npm:^3.0.2" + checksum: 10c0/110d273cbba2631c318762f02c159531f1e05e4af91080368b9d4c02b880824ae4c49a8ec7094c6434989ea2ee8de913b21c809279babeaf447fc90a682dcd7c + languageName: node + linkType: hard + "queue-microtask@npm:^1.2.2": version: 1.2.3 resolution: "queue-microtask@npm:1.2.3" @@ -2017,6 +2396,30 @@ __metadata: languageName: node linkType: hard +"react-reconciler@npm:^0.31.0": + version: 0.31.0 + resolution: "react-reconciler@npm:0.31.0" + dependencies: + scheduler: "npm:^0.25.0" + peerDependencies: + react: ^19.0.0 + checksum: 10c0/97920e1866c7206e200c3920c133c2e85f62a3c54fd9bc4b83c10c558d83d98eb378caab4fe37498e0cc1b1b2665d898627f2ae2537b29c8ab295ec8abc0c580 + languageName: node + linkType: hard + +"react-use-measure@npm:^2.1.7": + version: 2.1.7 + resolution: "react-use-measure@npm:2.1.7" + peerDependencies: + react: ">=16.13" + react-dom: ">=16.13" + peerDependenciesMeta: + react-dom: + optional: true + checksum: 10c0/ff24130e6f95e853feb6892fb74af08dbc5aae3574b701169e3bc3adb392c3162f51a58ddfe39bb7337db13ae609bbec0bb51a9de8b5fae5420f9d17e1f8b542 + languageName: node + linkType: hard + "react@npm:18.3.1": version: 18.3.1 resolution: "react@npm:18.3.1" @@ -2044,6 +2447,20 @@ __metadata: languageName: node linkType: hard +"regenerator-runtime@npm:^0.14.0": + version: 0.14.1 + resolution: "regenerator-runtime@npm:0.14.1" + checksum: 10c0/1b16eb2c4bceb1665c89de70dcb64126a22bc8eb958feef3cd68fe11ac6d2a4899b5cd1b80b0774c7c03591dc57d16631a7f69d2daa2ec98100e2f29f7ec4cc4 + languageName: node + linkType: hard + +"require-from-string@npm:^2.0.2": + version: 2.0.2 + resolution: "require-from-string@npm:2.0.2" + checksum: 10c0/aaa267e0c5b022fc5fd4eef49d8285086b15f2a1c54b28240fdf03599cbd9c26049fee3eab894f2e1f6ca65e513b030a7c264201e3f005601e80c49fb2937ce2 + languageName: node + linkType: hard + "resolve@npm:^1.1.7, resolve@npm:^1.22.8": version: 1.22.8 resolution: "resolve@npm:1.22.8" @@ -2109,6 +2526,13 @@ __metadata: languageName: node linkType: hard +"scheduler@npm:^0.25.0": + version: 0.25.0 + resolution: "scheduler@npm:0.25.0" + checksum: 10c0/a4bb1da406b613ce72c1299db43759526058fdcc413999c3c3e0db8956df7633acf395cb20eb2303b6a65d658d66b6585d344460abaee8080b4aa931f10eaafe + languageName: node + linkType: hard + "semver@npm:^7.3.5, semver@npm:^7.6.3": version: 7.6.3 resolution: "semver@npm:7.6.3" @@ -2270,6 +2694,26 @@ __metadata: languageName: node linkType: hard +"stats-gl@npm:^2.2.8": + version: 2.4.2 + resolution: "stats-gl@npm:2.4.2" + dependencies: + "@types/three": "npm:*" + three: "npm:^0.170.0" + peerDependencies: + "@types/three": "*" + three: "*" + checksum: 10c0/cff079103d4004cdfd72d65c977b7237a929b02f57b275081188e9313d74620b70ea0a57e0561fb6a43b1597830068b4adb2625dbc08b99799fd12a71ec54729 + languageName: node + linkType: hard + +"stats.js@npm:^0.17.0": + version: 0.17.0 + resolution: "stats.js@npm:0.17.0" + checksum: 10c0/03392b4b9eb15cb285b8cf46c5dd68e4e7b556d8e4c72a3a48e6bd2ab6f2e72a02b145d465f594450df081d8f94a45e8ac1112a0d507a200a5768504082ca080 + languageName: node + linkType: hard + "streamsearch@npm:^1.1.0": version: 1.1.0 resolution: "streamsearch@npm:1.1.0" @@ -2358,6 +2802,15 @@ __metadata: languageName: node linkType: hard +"suspend-react@npm:^0.1.3": + version: 0.1.3 + resolution: "suspend-react@npm:0.1.3" + peerDependencies: + react: ">=17.0" + checksum: 10c0/c35436f492a5cab85a2aac8f5ee5af8f67edbab3a49e12d15c7f770aae4cf09fe2925b3b8c122929210c1f28aa0958f91684a687d6608f116f7da8406d8a2f1c + languageName: node + linkType: hard + "tabbable@npm:^6.0.0": version: 6.2.0 resolution: "tabbable@npm:6.2.0" @@ -2430,6 +2883,45 @@ __metadata: languageName: node linkType: hard +"three-mesh-bvh@npm:^0.8.3": + version: 0.8.3 + resolution: "three-mesh-bvh@npm:0.8.3" + peerDependencies: + three: ">= 0.159.0" + checksum: 10c0/d1f052d4606498acf57e08e28d01c84157eb388d46be8e03bc839630dc4eb919d5bb5e2ed4e9113d39cbc3cda29f09212e3450e1bb21de6615ec330f2575fc28 + languageName: node + linkType: hard + +"three-stdlib@npm:^2.35.6": + version: 2.35.14 + resolution: "three-stdlib@npm:2.35.14" + dependencies: + "@types/draco3d": "npm:^1.4.0" + "@types/offscreencanvas": "npm:^2019.6.4" + "@types/webxr": "npm:^0.5.2" + draco3d: "npm:^1.4.1" + fflate: "npm:^0.6.9" + potpack: "npm:^1.0.1" + peerDependencies: + three: ">=0.128.0" + checksum: 10c0/a70c3a09bde3c292c3e172d52891f846df3dfb375e2868ff09dbbfe5771b17258f632748745ea788927d332f5e1a2822864cb66510f23542a9973c9ebc549f26 + languageName: node + linkType: hard + +"three@npm:^0.170.0": + version: 0.170.0 + resolution: "three@npm:0.170.0" + checksum: 10c0/eeaa1eccb31467654a044fb90634b3b6bb8d0768a4f029c082fb6aa22aec0d6362d0e3af74938caa1db512b7823a031b54a76e88fba6cfd535054165ea9667c9 + languageName: node + linkType: hard + +"three@npm:^0.174.0": + version: 0.174.0 + resolution: "three@npm:0.174.0" + checksum: 10c0/704c3aaa72a7e97ad4f829e81870c8add856ab5a8fb4fc34e391033c527df5cb87a283edb88e69d2d6974bee8afe732c616e879081c8ea232103aa806b97793b + languageName: node + linkType: hard + "to-regex-range@npm:^5.0.1": version: 5.0.1 resolution: "to-regex-range@npm:5.0.1" @@ -2439,6 +2931,36 @@ __metadata: languageName: node linkType: hard +"troika-three-text@npm:^0.52.0": + version: 0.52.3 + resolution: "troika-three-text@npm:0.52.3" + dependencies: + bidi-js: "npm:^1.0.2" + troika-three-utils: "npm:^0.52.0" + troika-worker-utils: "npm:^0.52.0" + webgl-sdf-generator: "npm:1.1.1" + peerDependencies: + three: ">=0.125.0" + checksum: 10c0/25c3ae9db859771896422d15d91f67e3b501ed65b6394ce4985a1bb68376c6f5d0dbc4a1da923dc96f6d760ec6544324f46123ca5d08bddbbccd0c63208d839d + languageName: node + linkType: hard + +"troika-three-utils@npm:^0.52.0": + version: 0.52.0 + resolution: "troika-three-utils@npm:0.52.0" + peerDependencies: + three: ">=0.125.0" + checksum: 10c0/6fe5de1e9540bb8c79314a49aee47fdd6f9e739992a6a04b111b364efdd0a8b15af6786d12512edf5b1a2a5f3e324906489533c0645d4d2c70a95e5ddf27b04f + languageName: node + linkType: hard + +"troika-worker-utils@npm:^0.52.0": + version: 0.52.0 + resolution: "troika-worker-utils@npm:0.52.0" + checksum: 10c0/bcd776324ce3e941c1a913531cee6022c867b71bc2d6bb67f1eadf6df96fd4f865c51b90ac58fc5c0ae3346a4bd8360b0a52398864800a7a9c60473151d452d5 + languageName: node + linkType: hard + "ts-interface-checker@npm:^0.1.9": version: 0.1.13 resolution: "ts-interface-checker@npm:0.1.13" @@ -2453,6 +2975,15 @@ __metadata: languageName: node linkType: hard +"tunnel-rat@npm:^0.1.2": + version: 0.1.2 + resolution: "tunnel-rat@npm:0.1.2" + dependencies: + zustand: "npm:^4.3.2" + checksum: 10c0/93cd50c7c9141e2662707602a21401145092e5a3c815b57a752937419ab6187a2ff36fa7e0f65e0c587022149bf2d323ace07dff61106511b7d4845e53390cc9 + languageName: node + linkType: hard + "typescript@npm:5.6.3": version: 5.6.3 resolution: "typescript@npm:5.6.3" @@ -2512,6 +3043,15 @@ __metadata: languageName: node linkType: hard +"use-sync-external-store@npm:^1.2.2, use-sync-external-store@npm:^1.4.0": + version: 1.4.0 + resolution: "use-sync-external-store@npm:1.4.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: 10c0/ec011a5055962c0f6b509d6e78c0b143f8cd069890ae370528753053c55e3b360d3648e76cfaa854faa7a59eb08d6c5fb1015e60ffde9046d32f5b2a295acea5 + languageName: node + linkType: hard + "util-deprecate@npm:^1.0.2": version: 1.0.2 resolution: "util-deprecate@npm:1.0.2" @@ -2519,6 +3059,27 @@ __metadata: languageName: node linkType: hard +"utility-types@npm:^3.11.0": + version: 3.11.0 + resolution: "utility-types@npm:3.11.0" + checksum: 10c0/2f1580137b0c3e6cf5405f37aaa8f5249961a76d26f1ca8efc0ff49a2fc0e0b2db56de8e521a174d075758e0c7eb3e590edec0832eb44478b958f09914920f19 + languageName: node + linkType: hard + +"webgl-constants@npm:^1.1.1": + version: 1.1.1 + resolution: "webgl-constants@npm:1.1.1" + checksum: 10c0/525a9838b0cbcbab902d1bc5457882424a9c146f851042c901ae457123193f5c5e47846b47c1c999e4f3abc24322cac494cea0cc52fec28bd8eb3a05cd79d573 + languageName: node + linkType: hard + +"webgl-sdf-generator@npm:1.1.1": + version: 1.1.1 + resolution: "webgl-sdf-generator@npm:1.1.1" + checksum: 10c0/769bfab22a9485a59bda9448d1d8684228adc9ebdac2cc69012a0854282385d1bef319f703758829e2c647e7532a9844e62977110e825bd75a3e0b8a0ee276ce + languageName: node + linkType: hard + "which@npm:^2.0.1": version: 2.0.2 resolution: "which@npm:2.0.2" @@ -2606,3 +3167,44 @@ __metadata: checksum: 10c0/b4239c8bf3988bfdaaed1c48f3958d0b047b721c4908a76bd78e73387d107963cda774541cf303c2ea89261481c995aa6666e7e77c30717ad440cdb499d9e5ca languageName: node linkType: hard + +"zustand@npm:^4.3.2": + version: 4.5.6 + resolution: "zustand@npm:4.5.6" + dependencies: + use-sync-external-store: "npm:^1.2.2" + peerDependencies: + "@types/react": ">=16.8" + immer: ">=9.0.6" + react: ">=16.8" + peerDependenciesMeta: + "@types/react": + optional: true + immer: + optional: true + react: + optional: true + checksum: 10c0/5b39aff2ef57e5a8ada647261ec1115697d397be311c51461d9ea81b5b63c6d2c498b960477ad2db72dc21db6aa229a92bdf644f6a8ecf7b1d71df5b4a5e95d3 + languageName: node + linkType: hard + +"zustand@npm:^5.0.1, zustand@npm:^5.0.3": + version: 5.0.3 + resolution: "zustand@npm:5.0.3" + peerDependencies: + "@types/react": ">=18.0.0" + immer: ">=9.0.6" + react: ">=18.0.0" + use-sync-external-store: ">=1.2.0" + peerDependenciesMeta: + "@types/react": + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + checksum: 10c0/dad96c6c123fda088c583d5df6692e9245cd207583078dc15f93d255a67b0f346bad4535545c98852ecde93d254812a0c799579dfde2ab595016b99fbe20e4d5 + languageName: node + linkType: hard From b86042ac8829cd4658a5178dc654cfeb403bc191 Mon Sep 17 00:00:00 2001 From: Domenico Mancini Date: Wed, 26 Mar 2025 16:39:32 +0100 Subject: [PATCH 02/10] feat: add tailwind theme interoperability --- dash/src/styles/tailwindTheme.tsx | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 dash/src/styles/tailwindTheme.tsx diff --git a/dash/src/styles/tailwindTheme.tsx b/dash/src/styles/tailwindTheme.tsx new file mode 100644 index 00000000..1528f9bd --- /dev/null +++ b/dash/src/styles/tailwindTheme.tsx @@ -0,0 +1,5 @@ +import tailwindConfig from "tailwind.config"; +import resolveConfig from "tailwindcss/resolveConfig"; + +const fullTailwindConfig = resolveConfig(tailwindConfig); +export const { theme } = fullTailwindConfig; From b3b6cfd2dde222e79895b09ff93e7a9c04872b13 Mon Sep 17 00:00:00 2001 From: Domenico Mancini Date: Wed, 26 Mar 2025 16:40:12 +0100 Subject: [PATCH 03/10] feat(settings): add toggle for 3d map --- dash/src/app/(nav)/settings/page.tsx | 19 ++++++++++++------- dash/src/stores/useSettingsStore.ts | 8 +++++++- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/dash/src/app/(nav)/settings/page.tsx b/dash/src/app/(nav)/settings/page.tsx index 36908358..56544d36 100644 --- a/dash/src/app/(nav)/settings/page.tsx +++ b/dash/src/app/(nav)/settings/page.tsx @@ -1,22 +1,22 @@ "use client"; -import { useEffect, useState } from "react"; import { motion } from "framer-motion"; import Image from "next/image"; +import { useEffect, useState } from "react"; import xIcon from "public/icons/xmark.svg"; import type { Driver } from "@/types/state.type"; -import SegmentedControls from "@/components/SegmentedControls"; -import SelectMultiple from "@/components/SelectMultiple"; -import DriverTag from "@/components/driver/DriverTag"; -import DelayInput from "@/components/DelayInput"; import Button from "@/components/Button"; -import Toggle from "@/components/Toggle"; +import DelayInput from "@/components/DelayInput"; import Footer from "@/components/Footer"; -import Slider from "@/components/Slider"; import Input from "@/components/Input"; +import SegmentedControls from "@/components/SegmentedControls"; +import SelectMultiple from "@/components/SelectMultiple"; +import Slider from "@/components/Slider"; +import Toggle from "@/components/Toggle"; +import DriverTag from "@/components/driver/DriverTag"; import { useSettingsStore } from "@/stores/useSettingsStore"; @@ -78,6 +78,11 @@ export default function SettingsPage() { )} +
+ settings.setUse3DMap(v)} /> +

(Experimental) Use 3D Map

+
+

Favorite Drivers

Select your favorite drivers to highlight them on the dashboard.

diff --git a/dash/src/stores/useSettingsStore.ts b/dash/src/stores/useSettingsStore.ts index b0902181..1b37724a 100644 --- a/dash/src/stores/useSettingsStore.ts +++ b/dash/src/stores/useSettingsStore.ts @@ -1,5 +1,5 @@ -import { persist, createJSONStorage, subscribeWithSelector } from "zustand/middleware"; import { create } from "zustand"; +import { createJSONStorage, persist, subscribeWithSelector } from "zustand/middleware"; type SpeedUnit = "metric" | "imperial"; @@ -34,6 +34,9 @@ type SettingsStore = { raceControlChimeVolume: number; setRaceControlChimeVolume: (raceControlChimeVolume: number) => void; + + use3DMap: boolean; + setUse3DMap: (use3DMap: boolean) => void; }; export const useSettingsStore = create( @@ -71,6 +74,9 @@ export const useSettingsStore = create( raceControlChimeVolume: 50, setRaceControlChimeVolume: (raceControlChimeVolume: number) => set({ raceControlChimeVolume }), + + use3DMap: false, + setUse3DMap: (use3DMap: boolean) => set({ use3DMap }), }), { name: "settings-storage", From 2ea6be3ab61066709f9d4b144b90865296df8540 Mon Sep 17 00:00:00 2001 From: Domenico Mancini Date: Wed, 26 Mar 2025 16:40:47 +0100 Subject: [PATCH 04/10] feat(map): add Map3D component --- dash/src/app/dashboard/page.tsx | 22 +- dash/src/components/map/CornerNumber3D.tsx | 37 +++ dash/src/components/map/DriverIndicator.tsx | 167 +++++++++++++ dash/src/components/{ => map}/Map.tsx | 30 ++- dash/src/components/map/Map3D.tsx | 248 ++++++++++++++++++++ dash/src/components/map/MapLoader.tsx | 7 + dash/src/lib/r3f.tsx | 6 + 7 files changed, 493 insertions(+), 24 deletions(-) create mode 100644 dash/src/components/map/CornerNumber3D.tsx create mode 100644 dash/src/components/map/DriverIndicator.tsx rename dash/src/components/{ => map}/Map.tsx (93%) create mode 100644 dash/src/components/map/Map3D.tsx create mode 100644 dash/src/components/map/MapLoader.tsx create mode 100644 dash/src/lib/r3f.tsx diff --git a/dash/src/app/dashboard/page.tsx b/dash/src/app/dashboard/page.tsx index 57a003c9..9772226a 100644 --- a/dash/src/app/dashboard/page.tsx +++ b/dash/src/app/dashboard/page.tsx @@ -1,27 +1,29 @@ "use client"; -import { useEffect, useRef } from "react"; import clsx from "clsx"; +import { useEffect, useRef } from "react"; import Fireworks, { FireworksHandlers } from "@fireworks-js/react"; import { useDataStore } from "@/stores/useDataStore"; -import SessionInfo from "@/components/SessionInfo"; -import WeatherInfo from "@/components/WeatherInfo"; -import TrackInfo from "@/components/TrackInfo"; +import Footer from "@/components/Footer"; +import LapCount from "@/components/LapCount"; import LeaderBoard from "@/components/LeaderBoard"; +import Map from "@/components/map/Map"; +import Map3D from "@/components/map/Map3D"; import Qualifying from "@/components/Qualifying"; import RaceControl from "@/components/RaceControl"; +import SessionInfo from "@/components/SessionInfo"; import TeamRadios from "@/components/TeamRadios"; -import Footer from "@/components/Footer"; -import Map from "@/components/Map"; -import LapCount from "@/components/LapCount"; +import TrackInfo from "@/components/TrackInfo"; +import WeatherInfo from "@/components/WeatherInfo"; +import { useSettingsStore } from "@/stores/useSettingsStore"; export default function Page() { const fireworksRef = useRef(null); const fireworksPlayed = useRef(false); - + const settings = useSettingsStore(); const sessionType = useDataStore((state) => state.sessionInfo?.type); const raceControlMessages = useDataStore((state) => state.raceControlMessages?.messages); @@ -83,9 +85,7 @@ export default function Page() { )} -
- -
+
{settings.use3DMap ? : }
(); + + // Make the text face the camera + useFrame(() => { + mesh.current?.lookAt(camera.position); + }); + + return ( + + {number} + + ); +} diff --git a/dash/src/components/map/DriverIndicator.tsx b/dash/src/components/map/DriverIndicator.tsx new file mode 100644 index 00000000..72ca036f --- /dev/null +++ b/dash/src/components/map/DriverIndicator.tsx @@ -0,0 +1,167 @@ +import { rotate } from "@/lib/map"; +import { toVector3 } from "@/lib/r3f"; +import { theme } from "@/styles/tailwindTheme"; +import { PositionCar } from "@/types/state.type"; +import { Sphere, Text as Text3D } from "@react-three/drei"; +import { useFrame, useThree } from "@react-three/fiber"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { Color, Mesh, MeshBasicMaterial, Vector3 } from "three"; + +function calculateAverageUpdateTime(history: number[]) { + if (history.length < 2) return 0; + + let totalAmount = 0; + for (let i = 0; i < history.length; ++i) { + const current = history[i]; + const previous = history[i - 1]; + const delta = current - previous; + totalAmount += delta; + } + + return totalAmount / (history.length - 1); +} + +type DriverIndicatorProps = { + name: string; + color: string | undefined; + favoriteDriver: boolean; + + pit: boolean; + hidden: boolean; + + pos: PositionCar; + rotation: number; + + centerX: number; + centerY: number; +}; + +export function DriverIndicator({ + pos, + name, + color, + favoriteDriver, + pit, + hidden, + rotation, + centerX, + centerY, +}: DriverIndicatorProps) { + const rotatedPos = rotate(pos.X, pos.Y, rotation, centerX, centerY); + const { camera } = useThree(); + + const mesh = useRef(); + + const opacity = useMemo(() => { + if (pit) return 0.2; + return hidden ? 0 : 1; + }, [pit, hidden]); + + const material = useMemo(() => { + const m = new MeshBasicMaterial(); + m.transparent = true; + m.opacity = opacity; + m.color = new Color(`#${color}`); + return m; + }, [color, opacity]); + + useFrame(() => { + mesh.current?.lookAt(camera.position); + }); + + return ( + + + {name} + + + + ); +} + +type InterpolatedDriverIndicatorProps = DriverIndicatorProps & { + interpolationLength?: number; +}; + +export function InterpolatedDriverIndicator({ + pos, + interpolationLength = 8, + ...props +}: InterpolatedDriverIndicatorProps) { + const incomingPosition = useMemo(() => new Vector3(pos.X, 0, pos.Y), [pos]); + const [currentPosition, setCurrentPosition] = useState(new Vector3()); + const [previousPosition, setPreviousPosition] = useState(new Vector3()); + const [interpolatedPosition, setInterpolatedPosition] = useState(new Vector3()); + const [interpolationFactor, setInterpolationFactor] = useState(0); + const [shouldUpdatePosition, setShouldUpdatePosition] = useState(false); + const updatesHistoryRef = useRef([]); + + useEffect(() => { + // Delegate update to the next frame + setShouldUpdatePosition(true); + }, [incomingPosition]); + + const updatePosition = useCallback(() => { + const { current: updatesHistory } = updatesHistoryRef; + + // Only set previous position if current position is valid + if (currentPosition.length() > 0) { + setPreviousPosition(currentPosition.clone()); + } + setCurrentPosition(incomingPosition.clone()); + setInterpolationFactor(0); + + // Record timestamp in history + const timestamp = performance.now(); + updatesHistory.push(timestamp); + if (updatesHistory.length > interpolationLength) { + updatesHistory.shift(); + } + + // Clear position update flag + setShouldUpdatePosition(false); + }, [currentPosition, incomingPosition, updatesHistoryRef]); + + useFrame((_, delta) => { + shouldUpdatePosition && updatePosition(); + + const { current: updatesHistory } = updatesHistoryRef; + + const averageTimeBetweenUpdatesInMillis = calculateAverageUpdateTime(updatesHistory) / 1000; + const interpolationRate = 1 / averageTimeBetweenUpdatesInMillis; + const interpolationSpeed = averageTimeBetweenUpdatesInMillis > 0 ? interpolationRate : 1; + + if (interpolationFactor < 1) { + // Interpolate position + const newInterpolatedPosition = new Vector3().lerpVectors(previousPosition, currentPosition, interpolationFactor); + setInterpolatedPosition(newInterpolatedPosition); + setInterpolationFactor((prev) => Math.min(1, prev + delta * interpolationSpeed)); + } else { + // Clamp to last known position + setInterpolatedPosition(currentPosition); + } + }); + + return ( + + ); +} diff --git a/dash/src/components/Map.tsx b/dash/src/components/map/Map.tsx similarity index 93% rename from dash/src/components/Map.tsx rename to dash/src/components/map/Map.tsx index 46a20a42..287b744c 100644 --- a/dash/src/components/Map.tsx +++ b/dash/src/components/map/Map.tsx @@ -1,13 +1,11 @@ -import { useEffect, useMemo, useState } from "react"; import clsx from "clsx"; +import { useEffect, useMemo, useState } from "react"; +import type { TrackPosition } from "@/types/map.type"; import type { PositionCar } from "@/types/state.type"; -import type { Map, TrackPosition } from "@/types/map.type"; -import { fetchMap } from "@/lib/fetchMap"; import { objectEntries } from "@/lib/driverHelper"; -import { useDataStore, usePositionStore } from "@/stores/useDataStore"; -import { useSettingsStore } from "@/stores/useSettingsStore"; +import { fetchMap } from "@/lib/fetchMap"; import { getTrackStatusMessage } from "@/lib/getTrackStatusMessage"; import { createSectors, @@ -18,6 +16,9 @@ import { rad, rotate, } from "@/lib/map"; +import { useDataStore, usePositionStore } from "@/stores/useDataStore"; +import { useSettingsStore } from "@/stores/useSettingsStore"; +import MapLoader from "./MapLoader"; // This is basically fearlessly copied from // https://github.com/tdjsnelling/monaco @@ -45,7 +46,7 @@ export default function Map() { const [[minX, minY, widthX, widthY], setBounds] = useState<(null | number)[]>([null, null, null, null]); const [[centerX, centerY], setCenter] = useState<(null | number)[]>([null, null]); - const [points, setPoints] = useState(null); + const [points, setPoints] = useState(null); const [sectors, setSectors] = useState([]); const [corners, setCorners] = useState([]); const [rotation, setRotation] = useState(0); @@ -111,18 +112,14 @@ export default function Map() { pulse: status?.pulse, number: sector.number, strokeWidth: color === "stroke-white" ? 60 : 120, - d: `M${sector.points[0].x},${sector.points[0].y} ${sector.points.map((point) => `L${point.x},${point.y}`).join(" ")}`, + d: generateDrawnSVGPath(sector.points), }; }) .sort(prioritizeColoredSectors); }, [trackStatus, sectors]); if (!points || !minX || !minY || !widthX || !widthY) { - return ( -
-
-
- ); + return ; } return ( @@ -136,7 +133,7 @@ export default function Map() { strokeWidth={300} strokeLinejoin="round" fill="transparent" - d={`M${points[0].x},${points[0].y} ${points.map((point) => `L${point.x},${point.y}`).join(" ")}`} + d={generateDrawnSVGPath(points)} /> {renderedSectors.map((sector) => { @@ -269,3 +266,10 @@ const CarDot = ({ pos, name, color, favoriteDriver, pit, hidden, rotation, cente ); }; + +function generateDrawnSVGPath(points: TrackPosition[]) { + const formattedCoords = points.map((point) => `${point.x},${point.y}`); + const origin = `M${formattedCoords[0]}`; + const spline = formattedCoords.map((coord) => `L${coord}`); + return `${origin} ${spline.join(" ")}`; +} diff --git a/dash/src/components/map/Map3D.tsx b/dash/src/components/map/Map3D.tsx new file mode 100644 index 00000000..5328525b --- /dev/null +++ b/dash/src/components/map/Map3D.tsx @@ -0,0 +1,248 @@ +import { useEffect, useMemo, useRef, useState } from "react"; + +import type { TrackPosition } from "@/types/map.type"; + +import { objectEntries } from "@/lib/driverHelper"; +import { fetchMap } from "@/lib/fetchMap"; +import { getTrackStatusMessage } from "@/lib/getTrackStatusMessage"; +import { createSectors, findYellowSectors, getSectorColor, MapSector, rad, rotate } from "@/lib/map"; +import { toVector3 } from "@/lib/r3f"; +import { useDataStore, usePositionStore } from "@/stores/useDataStore"; +import { useSettingsStore } from "@/stores/useSettingsStore"; +import { theme } from "@/styles/tailwindTheme"; +import { Line, OrbitControls } from "@react-three/drei"; +import { Canvas } from "@react-three/fiber"; +import { Vector3 } from "three"; +import CornerNumber from "./CornerNumber3D"; +import { InterpolatedDriverIndicator } from "./DriverIndicator"; +import MapLoader from "./MapLoader"; + +const SPACE = 1000; +const ROTATION_FIX = 90; +const CAMERA_FOV = 35; + +type Corner = { + number: number; + pos: TrackPosition; + labelPos: TrackPosition; +}; + +type RenderedSector3D = { + number: number; + points: TrackPosition[]; + color: string; + strokeWidth: number; + pulse?: number; +}; + +function prioritizeColoredSectors3D(a: RenderedSector3D, b: RenderedSector3D) { + if (a.color === "stroke-white" && b.color !== "stroke-white") { + return -1; + } + if (a.color !== "stroke-white" && b.color === "stroke-white") { + return 1; + } + return a.number - b.number; +} + +function getCenter(xPoints: number[], yPoints: number[]) { + const maxBounds = { + x: Math.max(...xPoints), + y: Math.max(...yPoints), + }; + + const minBounds = { + x: Math.min(...xPoints), + y: Math.min(...yPoints), + }; + + return { + x: (maxBounds.x - minBounds.x) * 0.5 + minBounds.x, + y: (maxBounds.y - minBounds.y) * 0.5 + minBounds.y, + }; +} + +export default function Map3D() { + const showCornerNumbers = useSettingsStore((state) => state.showCornerNumbers); + const favoriteDrivers = useSettingsStore((state) => state.favoriteDrivers); + + const positions = usePositionStore((state) => state.positions); + const drivers = useDataStore((state) => state?.driverList); + const trackStatus = useDataStore((state) => state?.trackStatus); + const timingDrivers = useDataStore((state) => state?.timingData); + const raceControlMessages = useDataStore((state) => state?.raceControlMessages?.messages); + const circuitKey = useDataStore((state) => state?.sessionInfo?.meeting.circuit.key); + + const [[minX, minY, widthX, widthY], setBounds] = useState<(null | number)[]>([null, null, null, null]); + const [[centerX, centerY], setCenter] = useState([0, 0]); + + const [points, setPoints] = useState(null); + const [sectors, setSectors] = useState([]); + const [corners, setCorners] = useState([]); + const [rotation, setRotation] = useState(0); + + // FIXME: This is garbage but I am struggling finding the proper type + const controlsRef = useRef(null); + + const defaultCameraDistance = useMemo(() => Math.max(widthX ?? 0, widthY ?? 0), [widthX, widthY]); + const defaultCameraPosition = useMemo( + () => [centerX, defaultCameraDistance, centerY + defaultCameraDistance] as const, + [centerX, centerY, defaultCameraDistance], + ); + const yellowSectors = useMemo(() => findYellowSectors(raceControlMessages), [raceControlMessages]); + const renderedSectors = useMemo(() => { + const status = getTrackStatusMessage(trackStatus?.status ? parseInt(trackStatus.status) : undefined); + return sectors + .map((sector) => { + const color = getSectorColor(sector, status?.bySector, status?.trackColor, yellowSectors); + return { + color, + pulse: status?.pulse, + number: sector.number, + strokeWidth: color === "stroke-white" ? 2 : 4, + points: sector.points, + }; + }) + .sort(prioritizeColoredSectors3D); + }, [trackStatus, sectors]); + + useEffect(() => { + const loadMap = async () => { + if (!circuitKey) return; + const mapJson = await fetchMap(circuitKey); + + const { x: mapCenterX, y: mapCenterY } = getCenter(mapJson.x, mapJson.y); + + const fixedRotation = mapJson.rotation + ROTATION_FIX; + + const sectors = createSectors(mapJson).map((s) => ({ + ...s, + start: rotate(s.start.x, s.start.y, fixedRotation, mapCenterX, mapCenterY), + end: rotate(s.end.x, s.end.y, fixedRotation, mapCenterX, mapCenterY), + points: s.points.map((p) => rotate(p.x, p.y, fixedRotation, mapCenterX, mapCenterY)), + })); + + const cornerPositions = mapJson.corners.map((corner) => ({ + number: corner.number, + pos: rotate(corner.trackPosition.x, corner.trackPosition.y, fixedRotation, mapCenterX, mapCenterY), + labelPos: rotate( + corner.trackPosition.x + 540 * Math.cos(rad(corner.angle)), + corner.trackPosition.y + 540 * Math.sin(rad(corner.angle)), + fixedRotation, + mapCenterX, + mapCenterY, + ), + })); + + const rotatedPoints = mapJson.x.map((x, index) => + rotate(x, mapJson.y[index], fixedRotation, mapCenterX, mapCenterY), + ); + + const pointsX = rotatedPoints.map((item) => item.x); + const pointsY = rotatedPoints.map((item) => item.y); + + const cMinX = Math.min(...pointsX) - SPACE; + const cMinY = Math.min(...pointsY) - SPACE; + const cWidthX = Math.max(...pointsX) - cMinX + SPACE * 2; + const cWidthY = Math.max(...pointsY) - cMinY + SPACE * 2; + + setCenter([mapCenterX, mapCenterY]); + setBounds([cMinX, cMinY, cWidthX, cWidthY]); + setSectors(sectors); + setPoints(rotatedPoints); + setRotation(fixedRotation); + setCorners(cornerPositions); + }; + + loadMap(); + }, [circuitKey]); + + if (!points || !minX || !minY || !widthX || !widthY) { + return ; + } + + return ( +
+
+ +
+ + + + + + toVector3(point))} + color={theme.colors.gray[800]} + lineWidth={2} + /> + {renderedSectors.map((sector) => ( + new Vector3(point.x, 0, point.y))} + color={sector.color} + lineWidth={sector.strokeWidth} + /> + ))} + {showCornerNumbers && + corners.map((corner) => ( + + ))} + + {centerX && centerY && positions && drivers && ( + <> + {objectEntries(drivers) + .reverse() + .filter((driver) => !!positions[driver.racingNumber].X && !!positions[driver.racingNumber].Y) + .map((driver) => { + const timingDriver = timingDrivers?.lines[driver.racingNumber]; + const hidden = timingDriver + ? timingDriver.knockedOut || timingDriver.stopped || timingDriver.retired + : false; + const pit = timingDriver?.inPit ?? false; + + return ( + 0 ? favoriteDrivers.includes(driver.racingNumber) : false} + name={driver.tla} + color={driver.teamColour} + pit={pit} + hidden={hidden} + pos={positions[driver.racingNumber]} + rotation={rotation} + centerX={centerX} + centerY={centerY} + /> + ); + })} + + )} + + +
+ ); +} diff --git a/dash/src/components/map/MapLoader.tsx b/dash/src/components/map/MapLoader.tsx new file mode 100644 index 00000000..d7a98410 --- /dev/null +++ b/dash/src/components/map/MapLoader.tsx @@ -0,0 +1,7 @@ +export default function MapLoader() { + return ( +
+
+
+ ); +} diff --git a/dash/src/lib/r3f.tsx b/dash/src/lib/r3f.tsx new file mode 100644 index 00000000..6f1eb58a --- /dev/null +++ b/dash/src/lib/r3f.tsx @@ -0,0 +1,6 @@ +import { TrackPosition } from "@/types/map.type"; +import { Vector3 } from "three"; + +export function toVector3(point: TrackPosition) { + return new Vector3(point.x, 0, point.y); +} From c58559b467aa9099ef2f9319d1321fa36d1ba878 Mon Sep 17 00:00:00 2001 From: Domenico Mancini Date: Thu, 27 Mar 2025 11:59:38 +0100 Subject: [PATCH 05/10] feat(map3d): add working sectors representation --- dash/src/components/TrackInfo.tsx | 16 +++- dash/src/components/map/CornerNumber3D.tsx | 8 +- dash/src/components/map/DriverIndicator.tsx | 5 +- dash/src/components/map/Map.tsx | 7 +- dash/src/components/map/Map3D.tsx | 23 +++-- dash/src/hooks/useLookAt.ts | 14 +++ dash/src/lib/getTrackStatusMessage.ts | 99 ++++++++++++++++----- dash/src/lib/map.ts | 9 +- dash/src/types/map.type.ts | 2 + 9 files changed, 135 insertions(+), 48 deletions(-) create mode 100644 dash/src/hooks/useLookAt.ts diff --git a/dash/src/components/TrackInfo.tsx b/dash/src/components/TrackInfo.tsx index d76021ed..d8105b7f 100644 --- a/dash/src/components/TrackInfo.tsx +++ b/dash/src/components/TrackInfo.tsx @@ -4,13 +4,21 @@ import clsx from "clsx"; import { useDataStore } from "@/stores/useDataStore"; -import { getTrackStatusMessage } from "@/lib/getTrackStatusMessage"; +import { getComputedTrackStyle, getTrackStatusMessage } from "@/lib/getTrackStatusMessage"; +import { useMemo } from "react"; export default function TrackInfo() { const lapCount = useDataStore((state) => state.lapCount); const track = useDataStore((state) => state.trackStatus); - const currentTrackStatus = getTrackStatusMessage(track?.status ? parseInt(track?.status) : undefined); + const currentTrackStatus = useMemo( + () => getTrackStatusMessage(track?.status ? parseInt(track?.status) : undefined), + [track], + ); + const computedTrackStyle = useMemo( + () => (currentTrackStatus ? getComputedTrackStyle(currentTrackStatus.trackColor) : null), + [currentTrackStatus], + ); return (
@@ -22,9 +30,9 @@ export default function TrackInfo() { {!!currentTrackStatus ? (

{currentTrackStatus.message}

diff --git a/dash/src/components/map/CornerNumber3D.tsx b/dash/src/components/map/CornerNumber3D.tsx index 3eab3325..f3d632c1 100644 --- a/dash/src/components/map/CornerNumber3D.tsx +++ b/dash/src/components/map/CornerNumber3D.tsx @@ -1,6 +1,7 @@ +import { useLookAt } from "@/hooks/useLookAt"; import { theme } from "@/styles/tailwindTheme"; import { Text as Text3D } from "@react-three/drei"; -import { useFrame, useThree } from "@react-three/fiber"; +import { useThree } from "@react-three/fiber"; import { useRef } from "react"; import { Mesh } from "three"; @@ -16,10 +17,7 @@ export default function CornerNumber({ number, x, y }: CornerNumberProps) { const mesh = useRef(); - // Make the text face the camera - useFrame(() => { - mesh.current?.lookAt(camera.position); - }); + useLookAt({ object: mesh.current, target: camera.position }); return ( { - mesh.current?.lookAt(camera.position); - }); + useLookAt({ object: mesh.current, target: camera.position }); return ( diff --git a/dash/src/components/map/Map.tsx b/dash/src/components/map/Map.tsx index 287b744c..de71ca2f 100644 --- a/dash/src/components/map/Map.tsx +++ b/dash/src/components/map/Map.tsx @@ -6,7 +6,7 @@ import type { PositionCar } from "@/types/state.type"; import { objectEntries } from "@/lib/driverHelper"; import { fetchMap } from "@/lib/fetchMap"; -import { getTrackStatusMessage } from "@/lib/getTrackStatusMessage"; +import { getTrackColorStroke, getTrackStatusMessage } from "@/lib/getTrackStatusMessage"; import { createSectors, findYellowSectors, @@ -107,11 +107,12 @@ export default function Map() { return sectors .map((sector) => { const color = getSectorColor(sector, status?.bySector, status?.trackColor, yellowSectors); + const stroke = getTrackColorStroke(color); return { - color, + color: stroke, pulse: status?.pulse, number: sector.number, - strokeWidth: color === "stroke-white" ? 60 : 120, + strokeWidth: color === "GREEN" ? 60 : 120, d: generateDrawnSVGPath(sector.points), }; }) diff --git a/dash/src/components/map/Map3D.tsx b/dash/src/components/map/Map3D.tsx index 5328525b..35f10899 100644 --- a/dash/src/components/map/Map3D.tsx +++ b/dash/src/components/map/Map3D.tsx @@ -1,10 +1,10 @@ import { useEffect, useMemo, useRef, useState } from "react"; -import type { TrackPosition } from "@/types/map.type"; +import type { TrackColor, TrackPosition } from "@/types/map.type"; import { objectEntries } from "@/lib/driverHelper"; import { fetchMap } from "@/lib/fetchMap"; -import { getTrackStatusMessage } from "@/lib/getTrackStatusMessage"; +import { getTrackColorHex, getTrackStatusMessage } from "@/lib/getTrackStatusMessage"; import { createSectors, findYellowSectors, getSectorColor, MapSector, rad, rotate } from "@/lib/map"; import { toVector3 } from "@/lib/r3f"; import { useDataStore, usePositionStore } from "@/stores/useDataStore"; @@ -30,16 +30,17 @@ type Corner = { type RenderedSector3D = { number: number; points: TrackPosition[]; - color: string; + trackColor: TrackColor; + hex: string; strokeWidth: number; pulse?: number; }; function prioritizeColoredSectors3D(a: RenderedSector3D, b: RenderedSector3D) { - if (a.color === "stroke-white" && b.color !== "stroke-white") { + if (a.trackColor === "GREEN" && b.trackColor !== "GREEN") { return -1; } - if (a.color !== "stroke-white" && b.color === "stroke-white") { + if (a.trackColor !== "GREEN" && b.trackColor === "GREEN") { return 1; } return a.number - b.number; @@ -89,17 +90,21 @@ export default function Map3D() { () => [centerX, defaultCameraDistance, centerY + defaultCameraDistance] as const, [centerX, centerY, defaultCameraDistance], ); - const yellowSectors = useMemo(() => findYellowSectors(raceControlMessages), [raceControlMessages]); + const yellowSectors = useMemo(() => { + findYellowSectors(raceControlMessages); + }, [raceControlMessages]); const renderedSectors = useMemo(() => { const status = getTrackStatusMessage(trackStatus?.status ? parseInt(trackStatus.status) : undefined); return sectors .map((sector) => { const color = getSectorColor(sector, status?.bySector, status?.trackColor, yellowSectors); + const hex = getTrackColorHex(color); return { - color, + hex, + trackColor: color, pulse: status?.pulse, number: sector.number, - strokeWidth: color === "stroke-white" ? 2 : 4, + strokeWidth: color === "GREEN" ? 2 : 5, points: sector.points, }; }) @@ -194,7 +199,7 @@ export default function Map3D() { renderOrder={1} key={sector.number} points={sector.points.map((point) => new Vector3(point.x, 0, point.y))} - color={sector.color} + color={sector.hex} lineWidth={sector.strokeWidth} /> ))} diff --git a/dash/src/hooks/useLookAt.ts b/dash/src/hooks/useLookAt.ts new file mode 100644 index 00000000..c795de13 --- /dev/null +++ b/dash/src/hooks/useLookAt.ts @@ -0,0 +1,14 @@ +import { useFrame } from "@react-three/fiber"; +import { Object3D, Vector3 } from "three"; + +type LookAtProps = { + object: Object3D | undefined | null; + target: Vector3; +}; + +/** + * Make a THREE object look at target. + */ +export function useLookAt({ object, target }: LookAtProps) { + useFrame(() => object?.lookAt(target)); +} diff --git a/dash/src/lib/getTrackStatusMessage.ts b/dash/src/lib/getTrackStatusMessage.ts index cfafb182..3137742a 100644 --- a/dash/src/lib/getTrackStatusMessage.ts +++ b/dash/src/lib/getTrackStatusMessage.ts @@ -1,32 +1,89 @@ +import { TrackColor } from "@/types/map.type"; + type StatusMessage = { message: string; - color: string; - trackColor: string; - bySector?: boolean; + trackColor: TrackColor; + bySector: boolean; pulse?: number; - hex: string; }; type MessageMap = { [key: string]: StatusMessage; }; -export const getTrackStatusMessage = (statusCode: number | undefined): StatusMessage | null => { - const messageMap: MessageMap = { - 1: { message: "Track Clear", color: "bg-emerald-500", trackColor: "stroke-white", hex: "#34b981" }, - 2: { - message: "Yellow Flag", - color: "bg-yellow-500", - trackColor: "stroke-yellow-500", - bySector: true, - hex: "#f59e0c", - }, - 3: { message: "Flag", color: "bg-yellow-500", trackColor: "stroke-yellow-500", bySector: true, hex: "#f59e0c" }, - 4: { message: "Safety Car", color: "bg-yellow-500", trackColor: "stroke-yellow-500", hex: "#f59e0c" }, - 5: { message: "Red Flag", color: "bg-red-500", trackColor: "stroke-red-500", hex: "#ef4444" }, - 6: { message: "VSC Deployed", color: "bg-yellow-500", trackColor: "stroke-yellow-500", hex: "#f59e0c" }, - 7: { message: "VSC Ending", color: "bg-yellow-500", trackColor: "stroke-yellow-500", hex: "#f59e0c" }, - }; +type ComputedTrackStyle = { + trackColor: TrackColor; + bg: string; + stroke: string; + trackHex: string; + flagHex: string; +}; + +const STROKE_BY_TRACK_COLOR: Record = { + GREEN: "stroke-white", + YELLOW: "stroke-yellow-500", + RED: "stroke-red-500", +}; - return statusCode ? (messageMap[statusCode] ?? messageMap[0]) : null; +const BG_BY_TRACK_COLOR: Record = { + GREEN: "bg-emerald-500", + YELLOW: "bg-yellow-500", + RED: "bg-red-500", }; + +const FLAG_HEX_BY_TRACK_COLOR: Record = { + GREEN: "#34b981", + YELLOW: "#f59e0c", + RED: "#ef4444", +}; + +const TRACK_HEX_BY_TRACK_COLOR: Record = { + GREEN: "#fafafa", + YELLOW: "#f59e0c", + RED: "#ef4444", +}; + +const MESSAGE_MAP: MessageMap = { + 1: { message: "Track Clear", trackColor: "GREEN", bySector: false }, + 2: { + message: "Yellow Flag", + trackColor: "YELLOW", + bySector: true, + }, + 3: { message: "Flag", trackColor: "YELLOW", bySector: true }, + 4: { message: "Safety Car", trackColor: "YELLOW", bySector: false }, + 5: { message: "Red Flag", trackColor: "RED", bySector: false }, + 6: { message: "VSC Deployed", trackColor: "YELLOW", bySector: false }, + 7: { message: "VSC Ending", trackColor: "YELLOW", bySector: false }, +}; + +export const getTrackStatusMessage = (statusCode: number | undefined): StatusMessage | null => { + if (!statusCode) return null; + return MESSAGE_MAP[statusCode] ?? MESSAGE_MAP[0]; +}; + +export function getTrackColorStroke(color: TrackColor) { + return STROKE_BY_TRACK_COLOR[color]; +} + +export function getTrackColorBg(color: TrackColor) { + return BG_BY_TRACK_COLOR[color]; +} + +export function getTrackColorHex(color: TrackColor) { + return TRACK_HEX_BY_TRACK_COLOR[color]; +} + +export function getTrackColorFlagHex(color: TrackColor) { + return FLAG_HEX_BY_TRACK_COLOR[color]; +} + +export function getComputedTrackStyle(color: TrackColor): ComputedTrackStyle { + return { + trackColor: color, + bg: getTrackColorBg(color), + stroke: getTrackColorStroke(color), + trackHex: getTrackColorHex(color), + flagHex: getTrackColorFlagHex(color), + }; +} diff --git a/dash/src/lib/map.ts b/dash/src/lib/map.ts index 1df61867..2a4aa4f6 100644 --- a/dash/src/lib/map.ts +++ b/dash/src/lib/map.ts @@ -1,4 +1,4 @@ -import type { Map, TrackPosition } from "@/types/map.type"; +import type { Map, TrackColor, TrackPosition } from "@/types/map.type"; import type { Message } from "@/types/state.type"; import { sortUtc } from "@/lib/sorting"; @@ -125,6 +125,9 @@ export const prioritizeColoredSectors = (a: RenderedSector, b: RenderedSector) = export const getSectorColor = ( sector: MapSector, bySector: boolean | undefined, - trackColor: string | undefined = "stroke-white", + trackColor: TrackColor | undefined = "GREEN", yellowSectors: Set, -) => (bySector ? (yellowSectors.has(sector.number) ? trackColor : "stroke-white") : trackColor); +): TrackColor => { + if (!bySector) return trackColor; + return yellowSectors.has(sector.number) ? trackColor : "GREEN"; +}; diff --git a/dash/src/types/map.type.ts b/dash/src/types/map.type.ts index ed409acd..a992dc32 100644 --- a/dash/src/types/map.type.ts +++ b/dash/src/types/map.type.ts @@ -42,3 +42,5 @@ export type TrackPosition = { x: number; y: number; }; + +export type TrackColor = "GREEN" | "YELLOW" | "RED"; \ No newline at end of file From ee1fc6d297c6529e0f7ee878740587064725e25d Mon Sep 17 00:00:00 2001 From: Domenico Mancini Date: Thu, 27 Mar 2025 12:05:34 +0100 Subject: [PATCH 06/10] fix(map3d): return correct yellow sector set instead of undefined --- dash/src/components/map/Map3D.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dash/src/components/map/Map3D.tsx b/dash/src/components/map/Map3D.tsx index 35f10899..1de2b4fd 100644 --- a/dash/src/components/map/Map3D.tsx +++ b/dash/src/components/map/Map3D.tsx @@ -90,9 +90,7 @@ export default function Map3D() { () => [centerX, defaultCameraDistance, centerY + defaultCameraDistance] as const, [centerX, centerY, defaultCameraDistance], ); - const yellowSectors = useMemo(() => { - findYellowSectors(raceControlMessages); - }, [raceControlMessages]); + const yellowSectors = useMemo(() => findYellowSectors(raceControlMessages), [raceControlMessages]); const renderedSectors = useMemo(() => { const status = getTrackStatusMessage(trackStatus?.status ? parseInt(trackStatus.status) : undefined); return sectors From b1a46ca3cfcfd9450126c81475f6897792e38877 Mon Sep 17 00:00:00 2001 From: Domenico Mancini Date: Fri, 28 Mar 2025 09:55:56 +0100 Subject: [PATCH 07/10] refactor(map): cleanup naming convention for utility methods --- dash/package.json | 1 + dash/src/components/TrackInfo.tsx | 4 +- dash/src/components/map/DriverIndicator.tsx | 41 +- dash/src/components/map/Map.tsx | 6 +- dash/src/components/map/Map3D.tsx | 16 +- dash/src/lib/getTrackStatusMessage.ts | 76 ++- dash/src/lib/map.ts | 10 +- dash/src/types/map.type.ts | 2 +- dash/yarn.lock | 510 +++++++++++++++++++- 9 files changed, 566 insertions(+), 100 deletions(-) diff --git a/dash/package.json b/dash/package.json index cb5c30a2..32a7932f 100644 --- a/dash/package.json +++ b/dash/package.json @@ -14,6 +14,7 @@ "@headlessui/react": "2.2.0", "@react-three/drei": "^10.0.5", "@react-three/fiber": "^9.1.0", + "@react-three/uikit": "^0.8.14", "@types/three": "^0.174.0", "clsx": "2.1.1", "framer-motion": "11.11.17", diff --git a/dash/src/components/TrackInfo.tsx b/dash/src/components/TrackInfo.tsx index d8105b7f..29c19c74 100644 --- a/dash/src/components/TrackInfo.tsx +++ b/dash/src/components/TrackInfo.tsx @@ -4,7 +4,7 @@ import clsx from "clsx"; import { useDataStore } from "@/stores/useDataStore"; -import { getComputedTrackStyle, getTrackStatusMessage } from "@/lib/getTrackStatusMessage"; +import { getComputedFlagStyle, getTrackStatusMessage } from "@/lib/getTrackStatusMessage"; import { useMemo } from "react"; export default function TrackInfo() { @@ -16,7 +16,7 @@ export default function TrackInfo() { [track], ); const computedTrackStyle = useMemo( - () => (currentTrackStatus ? getComputedTrackStyle(currentTrackStatus.trackColor) : null), + () => (currentTrackStatus ? getComputedFlagStyle(currentTrackStatus.flagType) : null), [currentTrackStatus], ); diff --git a/dash/src/components/map/DriverIndicator.tsx b/dash/src/components/map/DriverIndicator.tsx index 96324b42..6d8a86d7 100644 --- a/dash/src/components/map/DriverIndicator.tsx +++ b/dash/src/components/map/DriverIndicator.tsx @@ -1,12 +1,11 @@ -import { useLookAt } from "@/hooks/useLookAt"; import { rotate } from "@/lib/map"; import { toVector3 } from "@/lib/r3f"; import { theme } from "@/styles/tailwindTheme"; import { PositionCar } from "@/types/state.type"; -import { Sphere, Text as Text3D } from "@react-three/drei"; -import { useFrame, useThree } from "@react-three/fiber"; +import { Billboard, Sphere, Text as Text3D } from "@react-three/drei"; +import { useFrame } from "@react-three/fiber"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { Color, Mesh, MeshBasicMaterial, Vector3 } from "three"; +import { Color, MeshBasicMaterial, Vector3 } from "three"; function calculateAverageUpdateTime(history: number[]) { if (history.length < 2) return 0; @@ -49,9 +48,6 @@ export function DriverIndicator({ centerY, }: DriverIndicatorProps) { const rotatedPos = rotate(pos.X, pos.Y, rotation, centerX, centerY); - const { camera } = useThree(); - - const mesh = useRef(); const opacity = useMemo(() => { if (pit) return 0.2; @@ -66,24 +62,23 @@ export function DriverIndicator({ return m; }, [color, opacity]); - useLookAt({ object: mesh.current, target: camera.position }); - return ( - - {name} - + + + {name} + + ); diff --git a/dash/src/components/map/Map.tsx b/dash/src/components/map/Map.tsx index de71ca2f..50b56f7a 100644 --- a/dash/src/components/map/Map.tsx +++ b/dash/src/components/map/Map.tsx @@ -106,13 +106,13 @@ export default function Map() { return sectors .map((sector) => { - const color = getSectorColor(sector, status?.bySector, status?.trackColor, yellowSectors); - const stroke = getTrackColorStroke(color); + const flagType = getSectorColor(sector, status?.bySector, status?.flagType, yellowSectors); + const stroke = getTrackColorStroke(flagType); return { color: stroke, pulse: status?.pulse, number: sector.number, - strokeWidth: color === "GREEN" ? 60 : 120, + strokeWidth: flagType === "GREEN" ? 60 : 120, d: generateDrawnSVGPath(sector.points), }; }) diff --git a/dash/src/components/map/Map3D.tsx b/dash/src/components/map/Map3D.tsx index 1de2b4fd..e9a4a506 100644 --- a/dash/src/components/map/Map3D.tsx +++ b/dash/src/components/map/Map3D.tsx @@ -1,6 +1,6 @@ import { useEffect, useMemo, useRef, useState } from "react"; -import type { TrackColor, TrackPosition } from "@/types/map.type"; +import type { FlagType, TrackPosition } from "@/types/map.type"; import { objectEntries } from "@/lib/driverHelper"; import { fetchMap } from "@/lib/fetchMap"; @@ -30,17 +30,17 @@ type Corner = { type RenderedSector3D = { number: number; points: TrackPosition[]; - trackColor: TrackColor; + flagType: FlagType; hex: string; strokeWidth: number; pulse?: number; }; function prioritizeColoredSectors3D(a: RenderedSector3D, b: RenderedSector3D) { - if (a.trackColor === "GREEN" && b.trackColor !== "GREEN") { + if (a.flagType === "GREEN" && b.flagType !== "GREEN") { return -1; } - if (a.trackColor !== "GREEN" && b.trackColor === "GREEN") { + if (a.flagType !== "GREEN" && b.flagType === "GREEN") { return 1; } return a.number - b.number; @@ -95,14 +95,14 @@ export default function Map3D() { const status = getTrackStatusMessage(trackStatus?.status ? parseInt(trackStatus.status) : undefined); return sectors .map((sector) => { - const color = getSectorColor(sector, status?.bySector, status?.trackColor, yellowSectors); - const hex = getTrackColorHex(color); + const flagType = getSectorColor(sector, status?.bySector, status?.flagType, yellowSectors); + const hex = getTrackColorHex(flagType); return { hex, - trackColor: color, + flagType: flagType, pulse: status?.pulse, number: sector.number, - strokeWidth: color === "GREEN" ? 2 : 5, + strokeWidth: flagType === "GREEN" ? 2 : 5, points: sector.points, }; }) diff --git a/dash/src/lib/getTrackStatusMessage.ts b/dash/src/lib/getTrackStatusMessage.ts index 3137742a..1cf627f5 100644 --- a/dash/src/lib/getTrackStatusMessage.ts +++ b/dash/src/lib/getTrackStatusMessage.ts @@ -1,60 +1,41 @@ -import { TrackColor } from "@/types/map.type"; +import { FlagType } from "@/types/map.type"; type StatusMessage = { message: string; - trackColor: TrackColor; + flagType: FlagType; bySector: boolean; pulse?: number; }; -type MessageMap = { - [key: string]: StatusMessage; -}; +type MessageMap = Record; -type ComputedTrackStyle = { - trackColor: TrackColor; +type FlagStyle = { bg: string; stroke: string; trackHex: string; flagHex: string; }; -const STROKE_BY_TRACK_COLOR: Record = { - GREEN: "stroke-white", - YELLOW: "stroke-yellow-500", - RED: "stroke-red-500", -}; - -const BG_BY_TRACK_COLOR: Record = { - GREEN: "bg-emerald-500", - YELLOW: "bg-yellow-500", - RED: "bg-red-500", -}; - -const FLAG_HEX_BY_TRACK_COLOR: Record = { - GREEN: "#34b981", - YELLOW: "#f59e0c", - RED: "#ef4444", -}; +type ComputedFlagStyle = FlagStyle & { flag: FlagType }; -const TRACK_HEX_BY_TRACK_COLOR: Record = { - GREEN: "#fafafa", - YELLOW: "#f59e0c", - RED: "#ef4444", +const STYLE_BY_FLAG_TYPE: Record = { + GREEN: { stroke: "stroke-white", bg: "bg-emerald-500", flagHex: "#34b981", trackHex: "#fafafa" }, + YELLOW: { stroke: "stroke-yellow-500", bg: "bg-yellow-500", flagHex: "#f59e0c", trackHex: "#f59e0c" }, + RED: { stroke: "stroke-red-500", bg: "bg-red-500", flagHex: "#ef4444", trackHex: "#ef4444" }, }; const MESSAGE_MAP: MessageMap = { - 1: { message: "Track Clear", trackColor: "GREEN", bySector: false }, + 1: { message: "Track Clear", flagType: "GREEN", bySector: false }, 2: { message: "Yellow Flag", - trackColor: "YELLOW", + flagType: "YELLOW", bySector: true, }, - 3: { message: "Flag", trackColor: "YELLOW", bySector: true }, - 4: { message: "Safety Car", trackColor: "YELLOW", bySector: false }, - 5: { message: "Red Flag", trackColor: "RED", bySector: false }, - 6: { message: "VSC Deployed", trackColor: "YELLOW", bySector: false }, - 7: { message: "VSC Ending", trackColor: "YELLOW", bySector: false }, + 3: { message: "Flag", flagType: "YELLOW", bySector: true }, + 4: { message: "Safety Car", flagType: "YELLOW", bySector: false }, + 5: { message: "Red Flag", flagType: "RED", bySector: false }, + 6: { message: "VSC Deployed", flagType: "YELLOW", bySector: false }, + 7: { message: "VSC Ending", flagType: "YELLOW", bySector: false }, }; export const getTrackStatusMessage = (statusCode: number | undefined): StatusMessage | null => { @@ -62,28 +43,25 @@ export const getTrackStatusMessage = (statusCode: number | undefined): StatusMes return MESSAGE_MAP[statusCode] ?? MESSAGE_MAP[0]; }; -export function getTrackColorStroke(color: TrackColor) { - return STROKE_BY_TRACK_COLOR[color]; +export function getTrackColorStroke(flag: FlagType) { + return STYLE_BY_FLAG_TYPE[flag].stroke; } -export function getTrackColorBg(color: TrackColor) { - return BG_BY_TRACK_COLOR[color]; +export function getTrackColorBg(flag: FlagType) { + return STYLE_BY_FLAG_TYPE[flag].bg; } -export function getTrackColorHex(color: TrackColor) { - return TRACK_HEX_BY_TRACK_COLOR[color]; +export function getTrackColorHex(flag: FlagType) { + return STYLE_BY_FLAG_TYPE[flag].trackHex; } -export function getTrackColorFlagHex(color: TrackColor) { - return FLAG_HEX_BY_TRACK_COLOR[color]; +export function getTrackColorFlagHex(flag: FlagType) { + return STYLE_BY_FLAG_TYPE[flag].flagHex; } -export function getComputedTrackStyle(color: TrackColor): ComputedTrackStyle { +export function getComputedFlagStyle(flag: FlagType): ComputedFlagStyle { return { - trackColor: color, - bg: getTrackColorBg(color), - stroke: getTrackColorStroke(color), - trackHex: getTrackColorHex(color), - flagHex: getTrackColorFlagHex(color), + ...STYLE_BY_FLAG_TYPE[flag], + flag, }; } diff --git a/dash/src/lib/map.ts b/dash/src/lib/map.ts index 2a4aa4f6..212314c6 100644 --- a/dash/src/lib/map.ts +++ b/dash/src/lib/map.ts @@ -1,4 +1,4 @@ -import type { Map, TrackColor, TrackPosition } from "@/types/map.type"; +import type { FlagType, Map, TrackPosition } from "@/types/map.type"; import type { Message } from "@/types/state.type"; import { sortUtc } from "@/lib/sorting"; @@ -125,9 +125,9 @@ export const prioritizeColoredSectors = (a: RenderedSector, b: RenderedSector) = export const getSectorColor = ( sector: MapSector, bySector: boolean | undefined, - trackColor: TrackColor | undefined = "GREEN", + flagType: FlagType | undefined = "GREEN", yellowSectors: Set, -): TrackColor => { - if (!bySector) return trackColor; - return yellowSectors.has(sector.number) ? trackColor : "GREEN"; +): FlagType => { + if (!bySector) return flagType; + return yellowSectors.has(sector.number) ? flagType : "GREEN"; }; diff --git a/dash/src/types/map.type.ts b/dash/src/types/map.type.ts index a992dc32..8f454706 100644 --- a/dash/src/types/map.type.ts +++ b/dash/src/types/map.type.ts @@ -43,4 +43,4 @@ export type TrackPosition = { y: number; }; -export type TrackColor = "GREEN" | "YELLOW" | "RED"; \ No newline at end of file +export type FlagType = "GREEN" | "YELLOW" | "RED"; \ No newline at end of file diff --git a/dash/yarn.lock b/dash/yarn.lock index dc6c3561..1362772b 100644 --- a/dash/yarn.lock +++ b/dash/yarn.lock @@ -477,6 +477,36 @@ __metadata: languageName: node linkType: hard +"@pmndrs/msdfonts@npm:^0.8.14": + version: 0.8.14 + resolution: "@pmndrs/msdfonts@npm:0.8.14" + checksum: 10c0/3760f206afa667f9e9b79a6792290975449bb5ad32146c4b0b6391253acecec733ad987527ab9b3b13017cd0f71b549337289283e0e1e8aadc65656089c5fb4b + languageName: node + linkType: hard + +"@pmndrs/uikit@npm:^0.8.14": + version: 0.8.14 + resolution: "@pmndrs/uikit@npm:0.8.14" + dependencies: + "@pmndrs/msdfonts": "npm:^0.8.14" + "@preact/signals-core": "npm:^1.5.1" + inline-style-parser: "npm:^0.2.3" + node-html-parser: "npm:^6.1.13" + tw-to-css: "npm:^0.0.12" + yoga-layout: "npm:^3.2.1" + peerDependencies: + three: ">=0.160" + checksum: 10c0/1c2dbd259f07c5fa1c7ff67b9e0fd27ecac3617702a1077eb024f2d774ca13e0445af6b2994ecdd933ffdb27cb330599581f8a5fcde715f4994bed72a4a220e1 + languageName: node + linkType: hard + +"@preact/signals-core@npm:^1.5.1": + version: 1.8.0 + resolution: "@preact/signals-core@npm:1.8.0" + checksum: 10c0/fa773157621d881e7aefddb8dbded805d8203600211040cc4b5907726e288a163fa58558c1d36bc7085d75e6488af0a806322bbbed005d08be76a76f40244246 + languageName: node + linkType: hard + "@react-aria/focus@npm:^3.17.1": version: 3.18.4 resolution: "@react-aria/focus@npm:3.18.4" @@ -622,6 +652,28 @@ __metadata: languageName: node linkType: hard +"@react-three/uikit@npm:^0.8.14": + version: 0.8.14 + resolution: "@react-three/uikit@npm:0.8.14" + dependencies: + "@pmndrs/uikit": "npm:^0.8.14" + "@preact/signals-core": "npm:^1.5.1" + chalk: "npm:^5.3.0" + commander: "npm:^12.0.0" + ora: "npm:^8.0.1" + prettier: "npm:^3.2.5" + prompts: "npm:^2.4.2" + zod: "npm:^3.22.4" + zustand: "npm:^4.5.2" + peerDependencies: + "@react-three/fiber": ">=8" + react: ">=18" + bin: + uikit: dist/cli/index.js + checksum: 10c0/6d0c44a709e94c6c54631cda306314057343244d60dcec4e7c5e44177a1e20023df357ff9c4bd591199b90e7088a5ecf9b0296dc39a71b5b96ea9135e99ebf4b + languageName: node + linkType: hard + "@react-types/shared@npm:^3.25.0": version: 3.25.0 resolution: "@react-types/shared@npm:3.25.0" @@ -935,6 +987,13 @@ __metadata: languageName: node linkType: hard +"boolbase@npm:^1.0.0": + version: 1.0.0 + resolution: "boolbase@npm:1.0.0" + checksum: 10c0/e4b53deb4f2b85c52be0e21a273f2045c7b6a6ea002b0e139c744cb6f95e9ec044439a52883b0d74dedd1ff3da55ed140cfdddfed7fb0cccbed373de5dce1bcf + languageName: node + linkType: hard + "brace-expansion@npm:^2.0.1": version: 2.0.1 resolution: "brace-expansion@npm:2.0.1" @@ -1029,7 +1088,14 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:^3.6.0": +"chalk@npm:^5.3.0": + version: 5.4.1 + resolution: "chalk@npm:5.4.1" + checksum: 10c0/b23e88132c702f4855ca6d25cb5538b1114343e41472d5263ee8a37cccfccd9c4216d111e1097c6a27830407a1dc81fecdf2a56f2c63033d4dbbd88c10b0dcef + languageName: node + linkType: hard + +"chokidar@npm:^3.5.3, chokidar@npm:^3.6.0": version: 3.6.0 resolution: "chokidar@npm:3.6.0" dependencies: @@ -1062,6 +1128,22 @@ __metadata: languageName: node linkType: hard +"cli-cursor@npm:^5.0.0": + version: 5.0.0 + resolution: "cli-cursor@npm:5.0.0" + dependencies: + restore-cursor: "npm:^5.0.0" + checksum: 10c0/7ec62f69b79f6734ab209a3e4dbdc8af7422d44d360a7cb1efa8a0887bbe466a6e625650c466fe4359aee44dbe2dc0b6994b583d40a05d0808a5cb193641d220 + languageName: node + linkType: hard + +"cli-spinners@npm:^2.9.2": + version: 2.9.2 + resolution: "cli-spinners@npm:2.9.2" + checksum: 10c0/907a1c227ddf0d7a101e7ab8b300affc742ead4b4ebe920a5bf1bc6d45dce2958fcd195eb28fa25275062fe6fa9b109b93b63bc8033396ed3bcb50297008b3a3 + languageName: node + linkType: hard + "client-only@npm:0.0.1": version: 0.0.1 resolution: "client-only@npm:0.0.1" @@ -1112,6 +1194,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:^12.0.0": + version: 12.1.0 + resolution: "commander@npm:12.1.0" + checksum: 10c0/6e1996680c083b3b897bfc1cfe1c58dfbcd9842fd43e1aaf8a795fbc237f65efcc860a3ef457b318e73f29a4f4a28f6403c3d653d021d960e4632dd45bde54a9 + languageName: node + linkType: hard + "commander@npm:^4.0.0": version: 4.1.1 resolution: "commander@npm:4.1.1" @@ -1153,6 +1242,26 @@ __metadata: languageName: node linkType: hard +"css-select@npm:^5.1.0": + version: 5.1.0 + resolution: "css-select@npm:5.1.0" + dependencies: + boolbase: "npm:^1.0.0" + css-what: "npm:^6.1.0" + domhandler: "npm:^5.0.2" + domutils: "npm:^3.0.1" + nth-check: "npm:^2.0.1" + checksum: 10c0/551c60dba5b54054741032c1793b5734f6ba45e23ae9e82761a3c0ed1acbb8cfedfa443aaba3a3c1a54cac12b456d2012a09d2cd5f0e82e430454c1b9d84d500 + languageName: node + linkType: hard + +"css-what@npm:^6.1.0": + version: 6.1.0 + resolution: "css-what@npm:6.1.0" + checksum: 10c0/a09f5a6b14ba8dcf57ae9a59474722e80f20406c53a61e9aedb0eedc693b135113ffe2983f4efc4b5065ae639442e9ae88df24941ef159c218b231011d733746 + languageName: node + linkType: hard + "cssesc@npm:^3.0.0": version: 3.0.0 resolution: "cssesc@npm:3.0.0" @@ -1211,6 +1320,44 @@ __metadata: languageName: node linkType: hard +"dom-serializer@npm:^2.0.0": + version: 2.0.0 + resolution: "dom-serializer@npm:2.0.0" + dependencies: + domelementtype: "npm:^2.3.0" + domhandler: "npm:^5.0.2" + entities: "npm:^4.2.0" + checksum: 10c0/d5ae2b7110ca3746b3643d3ef60ef823f5f078667baf530cec096433f1627ec4b6fa8c072f09d079d7cda915fd2c7bc1b7b935681e9b09e591e1e15f4040b8e2 + languageName: node + linkType: hard + +"domelementtype@npm:^2.3.0": + version: 2.3.0 + resolution: "domelementtype@npm:2.3.0" + checksum: 10c0/686f5a9ef0fff078c1412c05db73a0dce096190036f33e400a07e2a4518e9f56b1e324f5c576a0a747ef0e75b5d985c040b0d51945ce780c0dd3c625a18cd8c9 + languageName: node + linkType: hard + +"domhandler@npm:^5.0.2, domhandler@npm:^5.0.3": + version: 5.0.3 + resolution: "domhandler@npm:5.0.3" + dependencies: + domelementtype: "npm:^2.3.0" + checksum: 10c0/bba1e5932b3e196ad6862286d76adc89a0dbf0c773e5ced1eb01f9af930c50093a084eff14b8de5ea60b895c56a04d5de8bbc4930c5543d029091916770b2d2a + languageName: node + linkType: hard + +"domutils@npm:^3.0.1": + version: 3.2.2 + resolution: "domutils@npm:3.2.2" + dependencies: + dom-serializer: "npm:^2.0.0" + domelementtype: "npm:^2.3.0" + domhandler: "npm:^5.0.3" + checksum: 10c0/47938f473b987ea71cd59e59626eb8666d3aa8feba5266e45527f3b636c7883cca7e582d901531961f742c519d7514636b7973353b648762b2e3bedbf235fada + languageName: node + linkType: hard + "draco3d@npm:^1.4.1": version: 1.5.7 resolution: "draco3d@npm:1.5.7" @@ -1232,6 +1379,13 @@ __metadata: languageName: node linkType: hard +"emoji-regex@npm:^10.3.0": + version: 10.4.0 + resolution: "emoji-regex@npm:10.4.0" + checksum: 10c0/a3fcedfc58bfcce21a05a5f36a529d81e88d602100145fcca3dc6f795e3c8acc4fc18fe773fbf9b6d6e9371205edb3afa2668ec3473fa2aa7fd47d2a9d46482d + languageName: node + linkType: hard + "emoji-regex@npm:^8.0.0": version: 8.0.0 resolution: "emoji-regex@npm:8.0.0" @@ -1255,6 +1409,13 @@ __metadata: languageName: node linkType: hard +"entities@npm:^4.2.0": + version: 4.5.0 + resolution: "entities@npm:4.5.0" + checksum: 10c0/5b039739f7621f5d1ad996715e53d964035f75ad3b9a4d38c6b3804bb226e282ffeae2443624d8fdd9c47d8e926ae9ac009c54671243f0c3294c26af7cc85250 + languageName: node + linkType: hard + "env-paths@npm:^2.2.0": version: 2.2.1 resolution: "env-paths@npm:2.2.1" @@ -1276,6 +1437,13 @@ __metadata: languageName: node linkType: hard +"escape-string-regexp@npm:^1.0.3": + version: 1.0.5 + resolution: "escape-string-regexp@npm:1.0.5" + checksum: 10c0/a968ad453dd0c2724e14a4f20e177aaf32bb384ab41b674a8454afe9a41c5e6fe8903323e0a1052f56289d04bd600f81278edf140b0fcc02f5cac98d0f5b5371 + languageName: node + linkType: hard + "exponential-backoff@npm:^3.1.1": version: 3.1.1 resolution: "exponential-backoff@npm:3.1.1" @@ -1283,6 +1451,13 @@ __metadata: languageName: node linkType: hard +"extend@npm:^3.0.1": + version: 3.0.2 + resolution: "extend@npm:3.0.2" + checksum: 10c0/73bf6e27406e80aa3e85b0d1c4fd987261e628064e170ca781125c0b635a3dabad5e05adbf07595ea0cf1e6c5396cacb214af933da7cbaf24fe75ff14818e8f9 + languageName: node + linkType: hard + "f1-dash@workspace:.": version: 0.0.0-use.local resolution: "f1-dash@workspace:." @@ -1291,6 +1466,7 @@ __metadata: "@headlessui/react": "npm:2.2.0" "@react-three/drei": "npm:^10.0.5" "@react-three/fiber": "npm:^9.1.0" + "@react-three/uikit": "npm:^0.8.14" "@types/lodash": "npm:4.17.13" "@types/node": "npm:20.11.10" "@types/pako": "npm:2.0.3" @@ -1318,6 +1494,19 @@ __metadata: languageName: unknown linkType: soft +"fast-glob@npm:^3.2.12": + version: 3.3.3 + resolution: "fast-glob@npm:3.3.3" + dependencies: + "@nodelib/fs.stat": "npm:^2.0.2" + "@nodelib/fs.walk": "npm:^1.2.3" + glob-parent: "npm:^5.1.2" + merge2: "npm:^1.3.0" + micromatch: "npm:^4.0.8" + checksum: 10c0/f6aaa141d0d3384cf73cbcdfc52f475ed293f6d5b65bfc5def368b09163a9f7e5ec2b3014d80f733c405f58e470ee0cc451c2937685045cddcdeaa24199c43fe + languageName: node + linkType: hard + "fast-glob@npm:^3.3.2": version: 3.3.2 resolution: "fast-glob@npm:3.3.2" @@ -1460,6 +1649,13 @@ __metadata: languageName: node linkType: hard +"get-east-asian-width@npm:^1.0.0": + version: 1.3.0 + resolution: "get-east-asian-width@npm:1.3.0" + checksum: 10c0/1a049ba697e0f9a4d5514c4623781c5246982bdb61082da6b5ae6c33d838e52ce6726407df285cdbb27ec1908b333cf2820989bd3e986e37bb20979437fdf34b + languageName: node + linkType: hard + "glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2": version: 5.1.2 resolution: "glob-parent@npm:5.1.2" @@ -1517,6 +1713,15 @@ __metadata: languageName: node linkType: hard +"he@npm:1.2.0": + version: 1.2.0 + resolution: "he@npm:1.2.0" + bin: + he: bin/he + checksum: 10c0/a27d478befe3c8192f006cdd0639a66798979dfa6e2125c6ac582a19a5ebfec62ad83e8382e6036170d873f46e4536a7e795bf8b95bf7c247f4cc0825ccc8c17 + languageName: node + linkType: hard + "hls.js@npm:^1.5.17": version: 1.5.20 resolution: "hls.js@npm:1.5.20" @@ -1588,6 +1793,13 @@ __metadata: languageName: node linkType: hard +"inline-style-parser@npm:^0.2.3": + version: 0.2.4 + resolution: "inline-style-parser@npm:0.2.4" + checksum: 10c0/ddc0b210eaa03e0f98d677b9836242c583c7c6051e84ce0e704ae4626e7871c5b78f8e30853480218b446355745775df318d4f82d33087ff7e393245efa9a881 + languageName: node + linkType: hard + "ip-address@npm:^9.0.5": version: 9.0.5 resolution: "ip-address@npm:9.0.5" @@ -1623,6 +1835,15 @@ __metadata: languageName: node linkType: hard +"is-core-module@npm:^2.16.0": + version: 2.16.1 + resolution: "is-core-module@npm:2.16.1" + dependencies: + hasown: "npm:^2.0.2" + checksum: 10c0/898443c14780a577e807618aaae2b6f745c8538eca5c7bc11388a3f2dc6de82b9902bcc7eb74f07be672b11bbe82dd6a6edded44a00cb3d8f933d0459905eedd + languageName: node + linkType: hard + "is-extglob@npm:^2.1.1": version: 2.1.1 resolution: "is-extglob@npm:2.1.1" @@ -1646,6 +1867,13 @@ __metadata: languageName: node linkType: hard +"is-interactive@npm:^2.0.0": + version: 2.0.0 + resolution: "is-interactive@npm:2.0.0" + checksum: 10c0/801c8f6064f85199dc6bf99b5dd98db3282e930c3bc197b32f2c5b89313bb578a07d1b8a01365c4348c2927229234f3681eb861b9c2c92bee72ff397390fa600 + languageName: node + linkType: hard + "is-lambda@npm:^1.0.1": version: 1.0.1 resolution: "is-lambda@npm:1.0.1" @@ -1667,6 +1895,20 @@ __metadata: languageName: node linkType: hard +"is-unicode-supported@npm:^1.3.0": + version: 1.3.0 + resolution: "is-unicode-supported@npm:1.3.0" + checksum: 10c0/b8674ea95d869f6faabddc6a484767207058b91aea0250803cbf1221345cb0c56f466d4ecea375dc77f6633d248d33c47bd296fb8f4cdba0b4edba8917e83d8a + languageName: node + linkType: hard + +"is-unicode-supported@npm:^2.0.0": + version: 2.1.0 + resolution: "is-unicode-supported@npm:2.1.0" + checksum: 10c0/a0f53e9a7c1fdbcf2d2ef6e40d4736fdffff1c9f8944c75e15425118ff3610172c87bf7bc6c34d3903b04be59790bb2212ddbe21ee65b5a97030fc50370545a5 + languageName: node + linkType: hard + "isexe@npm:^2.0.0": version: 2.0.0 resolution: "isexe@npm:2.0.0" @@ -1705,6 +1947,15 @@ __metadata: languageName: node linkType: hard +"jiti@npm:^1.18.2": + version: 1.21.7 + resolution: "jiti@npm:1.21.7" + bin: + jiti: bin/jiti.js + checksum: 10c0/77b61989c758ff32407cdae8ddc77f85e18e1a13fc4977110dbd2e05fc761842f5f71bce684d9a01316e1c4263971315a111385759951080bbfe17cbb5de8f7a + languageName: node + linkType: hard + "jiti@npm:^1.21.6": version: 1.21.6 resolution: "jiti@npm:1.21.6" @@ -1728,6 +1979,13 @@ __metadata: languageName: node linkType: hard +"kleur@npm:^3.0.3": + version: 3.0.3 + resolution: "kleur@npm:3.0.3" + checksum: 10c0/cd3a0b8878e7d6d3799e54340efe3591ca787d9f95f109f28129bdd2915e37807bf8918bb295ab86afb8c82196beec5a1adcaf29042ce3f2bd932b038fe3aa4b + languageName: node + linkType: hard + "lie@npm:^3.0.2": version: 3.3.0 resolution: "lie@npm:3.3.0" @@ -1758,6 +2016,16 @@ __metadata: languageName: node linkType: hard +"log-symbols@npm:^6.0.0": + version: 6.0.0 + resolution: "log-symbols@npm:6.0.0" + dependencies: + chalk: "npm:^5.3.0" + is-unicode-supported: "npm:^1.3.0" + checksum: 10c0/36636cacedba8f067d2deb4aad44e91a89d9efb3ead27e1846e7b82c9a10ea2e3a7bd6ce28a7ca616bebc60954ff25c67b0f92d20a6a746bb3cc52c3701891f6 + languageName: node + linkType: hard + "loose-envify@npm:^1.1.0": version: 1.4.0 resolution: "loose-envify@npm:1.4.0" @@ -1829,7 +2097,7 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:^4.0.4, micromatch@npm:^4.0.8": +"micromatch@npm:^4.0.4, micromatch@npm:^4.0.5, micromatch@npm:^4.0.8": version: 4.0.8 resolution: "micromatch@npm:4.0.8" dependencies: @@ -1839,6 +2107,13 @@ __metadata: languageName: node linkType: hard +"mimic-function@npm:^5.0.0": + version: 5.0.1 + resolution: "mimic-function@npm:5.0.1" + checksum: 10c0/f3d9464dd1816ecf6bdf2aec6ba32c0728022039d992f178237d8e289b48764fee4131319e72eedd4f7f094e22ded0af836c3187a7edc4595d28dd74368fd81d + languageName: node + linkType: hard + "minimatch@npm:^9.0.4": version: 9.0.5 resolution: "minimatch@npm:9.0.5" @@ -1975,6 +2250,15 @@ __metadata: languageName: node linkType: hard +"nanoid@npm:^3.3.8": + version: 3.3.11 + resolution: "nanoid@npm:3.3.11" + bin: + nanoid: bin/nanoid.cjs + checksum: 10c0/40e7f70b3d15f725ca072dfc4f74e81fcf1fbb02e491cf58ac0c79093adc9b0a73b152bcde57df4b79cd097e13023d7504acb38404a4da7bc1cd8e887b82fe0b + languageName: node + linkType: hard + "negotiator@npm:^0.6.3": version: 0.6.4 resolution: "negotiator@npm:0.6.4" @@ -2063,6 +2347,16 @@ __metadata: languageName: node linkType: hard +"node-html-parser@npm:^6.1.13": + version: 6.1.13 + resolution: "node-html-parser@npm:6.1.13" + dependencies: + css-select: "npm:^5.1.0" + he: "npm:1.2.0" + checksum: 10c0/ca36290507da8ec0fa131a0fd67ba62e300365c3950b5a6058b0e32b71b520ff43a5661b19e98a5c9797dbe3428b08db788b602f1f8aa62f2db5bb66e1d80782 + languageName: node + linkType: hard + "node-releases@npm:^2.0.18": version: 2.0.18 resolution: "node-releases@npm:2.0.18" @@ -2095,6 +2389,15 @@ __metadata: languageName: node linkType: hard +"nth-check@npm:^2.0.1": + version: 2.1.1 + resolution: "nth-check@npm:2.1.1" + dependencies: + boolbase: "npm:^1.0.0" + checksum: 10c0/5fee7ff309727763689cfad844d979aedd2204a817fbaaf0e1603794a7c20db28548d7b024692f953557df6ce4a0ee4ae46cd8ebd9b36cfb300b9226b567c479 + languageName: node + linkType: hard + "object-assign@npm:^4.0.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" @@ -2109,6 +2412,32 @@ __metadata: languageName: node linkType: hard +"onetime@npm:^7.0.0": + version: 7.0.0 + resolution: "onetime@npm:7.0.0" + dependencies: + mimic-function: "npm:^5.0.0" + checksum: 10c0/5cb9179d74b63f52a196a2e7037ba2b9a893245a5532d3f44360012005c9cadb60851d56716ebff18a6f47129dab7168022445df47c2aff3b276d92585ed1221 + languageName: node + linkType: hard + +"ora@npm:^8.0.1": + version: 8.2.0 + resolution: "ora@npm:8.2.0" + dependencies: + chalk: "npm:^5.3.0" + cli-cursor: "npm:^5.0.0" + cli-spinners: "npm:^2.9.2" + is-interactive: "npm:^2.0.0" + is-unicode-supported: "npm:^2.0.0" + log-symbols: "npm:^6.0.0" + stdin-discarder: "npm:^0.2.2" + string-width: "npm:^7.2.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/7d9291255db22e293ea164f520b6042a3e906576ab06c9cf408bf9ef5664ba0a9f3bd258baa4ada058cfcc2163ef9b6696d51237a866682ce33295349ba02c3a + languageName: node + linkType: hard + "p-map@npm:^4.0.0": version: 4.0.0 resolution: "p-map@npm:4.0.0" @@ -2184,6 +2513,19 @@ __metadata: languageName: node linkType: hard +"postcss-css-variables@npm:0.18.0": + version: 0.18.0 + resolution: "postcss-css-variables@npm:0.18.0" + dependencies: + balanced-match: "npm:^1.0.0" + escape-string-regexp: "npm:^1.0.3" + extend: "npm:^3.0.1" + peerDependencies: + postcss: ^8.2.6 + checksum: 10c0/be3e6a752b852c20fdf7e76beb46b943da79b3c647a08df2b58e946ec1e52e229ac58646611e6cc746028dc949ca02ecb0772f5755743af5dfb5917c59cf7380 + languageName: node + linkType: hard + "postcss-import@npm:^15.1.0": version: 15.1.0 resolution: "postcss-import@npm:15.1.0" @@ -2208,7 +2550,7 @@ __metadata: languageName: node linkType: hard -"postcss-load-config@npm:^4.0.2": +"postcss-load-config@npm:^4.0.1, postcss-load-config@npm:^4.0.2": version: 4.0.2 resolution: "postcss-load-config@npm:4.0.2" dependencies: @@ -2226,7 +2568,7 @@ __metadata: languageName: node linkType: hard -"postcss-nested@npm:^6.2.0": +"postcss-nested@npm:^6.0.1, postcss-nested@npm:^6.2.0": version: 6.2.0 resolution: "postcss-nested@npm:6.2.0" dependencies: @@ -2237,7 +2579,7 @@ __metadata: languageName: node linkType: hard -"postcss-selector-parser@npm:^6.1.1, postcss-selector-parser@npm:^6.1.2": +"postcss-selector-parser@npm:^6.0.11, postcss-selector-parser@npm:^6.1.1, postcss-selector-parser@npm:^6.1.2": version: 6.1.2 resolution: "postcss-selector-parser@npm:6.1.2" dependencies: @@ -2276,6 +2618,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.4.23": + version: 8.5.3 + resolution: "postcss@npm:8.5.3" + dependencies: + nanoid: "npm:^3.3.8" + picocolors: "npm:^1.1.1" + source-map-js: "npm:^1.2.1" + checksum: 10c0/b75510d7b28c3ab728c8733dd01538314a18c52af426f199a3c9177e63eb08602a3938bfb66b62dc01350b9aed62087eabbf229af97a1659eb8d3513cec823b3 + languageName: node + linkType: hard + "potpack@npm:^1.0.1": version: 1.0.2 resolution: "potpack@npm:1.0.2" @@ -2350,6 +2703,15 @@ __metadata: languageName: node linkType: hard +"prettier@npm:^3.2.5": + version: 3.5.3 + resolution: "prettier@npm:3.5.3" + bin: + prettier: bin/prettier.cjs + checksum: 10c0/3880cb90b9dc0635819ab52ff571518c35bd7f15a6e80a2054c05dbc8a3aa6e74f135519e91197de63705bcb38388ded7e7230e2178432a1468005406238b877 + languageName: node + linkType: hard + "proc-log@npm:^4.1.0, proc-log@npm:^4.2.0": version: 4.2.0 resolution: "proc-log@npm:4.2.0" @@ -2377,6 +2739,16 @@ __metadata: languageName: node linkType: hard +"prompts@npm:^2.4.2": + version: 2.4.2 + resolution: "prompts@npm:2.4.2" + dependencies: + kleur: "npm:^3.0.3" + sisteransi: "npm:^1.0.5" + checksum: 10c0/16f1ac2977b19fe2cf53f8411cc98db7a3c8b115c479b2ca5c82b5527cd937aa405fa04f9a5960abeb9daef53191b53b4d13e35c1f5d50e8718c76917c5f1ea4 + languageName: node + linkType: hard + "queue-microtask@npm:^1.2.2": version: 1.2.3 resolution: "queue-microtask@npm:1.2.3" @@ -2474,6 +2846,19 @@ __metadata: languageName: node linkType: hard +"resolve@npm:^1.22.2": + version: 1.22.10 + resolution: "resolve@npm:1.22.10" + dependencies: + is-core-module: "npm:^2.16.0" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10c0/8967e1f4e2cc40f79b7e080b4582b9a8c5ee36ffb46041dccb20e6461161adf69f843b43067b4a375de926a2cd669157e29a29578191def399dd5ef89a1b5203 + languageName: node + linkType: hard + "resolve@patch:resolve@npm%3A^1.1.7#optional!builtin, resolve@patch:resolve@npm%3A^1.22.8#optional!builtin": version: 1.22.8 resolution: "resolve@patch:resolve@npm%3A1.22.8#optional!builtin::version=1.22.8&hash=c3c19d" @@ -2487,6 +2872,29 @@ __metadata: languageName: node linkType: hard +"resolve@patch:resolve@npm%3A^1.22.2#optional!builtin": + version: 1.22.10 + resolution: "resolve@patch:resolve@npm%3A1.22.10#optional!builtin::version=1.22.10&hash=c3c19d" + dependencies: + is-core-module: "npm:^2.16.0" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10c0/52a4e505bbfc7925ac8f4cd91fd8c4e096b6a89728b9f46861d3b405ac9a1ccf4dcbf8befb4e89a2e11370dacd0160918163885cbc669369590f2f31f4c58939 + languageName: node + linkType: hard + +"restore-cursor@npm:^5.0.0": + version: 5.1.0 + resolution: "restore-cursor@npm:5.1.0" + dependencies: + onetime: "npm:^7.0.0" + signal-exit: "npm:^4.1.0" + checksum: 10c0/c2ba89131eea791d1b25205bdfdc86699767e2b88dee2a590b1a6caa51737deac8bad0260a5ded2f7c074b7db2f3a626bcf1fcf3cdf35974cbeea5e2e6764f60 + languageName: node + linkType: hard + "retry@npm:^0.12.0": version: 0.12.0 resolution: "retry@npm:0.12.0" @@ -2627,7 +3035,7 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^4.0.1": +"signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0": version: 4.1.0 resolution: "signal-exit@npm:4.1.0" checksum: 10c0/41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 @@ -2643,6 +3051,13 @@ __metadata: languageName: node linkType: hard +"sisteransi@npm:^1.0.5": + version: 1.0.5 + resolution: "sisteransi@npm:1.0.5" + checksum: 10c0/230ac975cca485b7f6fe2b96a711aa62a6a26ead3e6fb8ba17c5a00d61b8bed0d7adc21f5626b70d7c33c62ff4e63933017a6462942c719d1980bb0b1207ad46 + languageName: node + linkType: hard + "smart-buffer@npm:^4.2.0": version: 4.2.0 resolution: "smart-buffer@npm:4.2.0" @@ -2714,6 +3129,13 @@ __metadata: languageName: node linkType: hard +"stdin-discarder@npm:^0.2.2": + version: 0.2.2 + resolution: "stdin-discarder@npm:0.2.2" + checksum: 10c0/c78375e82e956d7a64be6e63c809c7f058f5303efcaf62ea48350af072bacdb99c06cba39209b45a071c1acbd49116af30df1df9abb448df78a6005b72f10537 + languageName: node + linkType: hard + "streamsearch@npm:^1.1.0": version: 1.1.0 resolution: "streamsearch@npm:1.1.0" @@ -2743,6 +3165,17 @@ __metadata: languageName: node linkType: hard +"string-width@npm:^7.2.0": + version: 7.2.0 + resolution: "string-width@npm:7.2.0" + dependencies: + emoji-regex: "npm:^10.3.0" + get-east-asian-width: "npm:^1.0.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/eb0430dd43f3199c7a46dcbf7a0b34539c76fe3aa62763d0b0655acdcbdf360b3f66f3d58ca25ba0205f42ea3491fa00f09426d3b7d3040e506878fc7664c9b9 + languageName: node + linkType: hard + "strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": version: 6.0.1 resolution: "strip-ansi@npm:6.0.1" @@ -2752,7 +3185,7 @@ __metadata: languageName: node linkType: hard -"strip-ansi@npm:^7.0.1": +"strip-ansi@npm:^7.0.1, strip-ansi@npm:^7.1.0": version: 7.1.0 resolution: "strip-ansi@npm:7.1.0" dependencies: @@ -2777,7 +3210,7 @@ __metadata: languageName: node linkType: hard -"sucrase@npm:^3.35.0": +"sucrase@npm:^3.32.0, sucrase@npm:^3.35.0": version: 3.35.0 resolution: "sucrase@npm:3.35.0" dependencies: @@ -2818,6 +3251,40 @@ __metadata: languageName: node linkType: hard +"tailwindcss@npm:3.3.2": + version: 3.3.2 + resolution: "tailwindcss@npm:3.3.2" + dependencies: + "@alloc/quick-lru": "npm:^5.2.0" + arg: "npm:^5.0.2" + chokidar: "npm:^3.5.3" + didyoumean: "npm:^1.2.2" + dlv: "npm:^1.1.3" + fast-glob: "npm:^3.2.12" + glob-parent: "npm:^6.0.2" + is-glob: "npm:^4.0.3" + jiti: "npm:^1.18.2" + lilconfig: "npm:^2.1.0" + micromatch: "npm:^4.0.5" + normalize-path: "npm:^3.0.0" + object-hash: "npm:^3.0.0" + picocolors: "npm:^1.0.0" + postcss: "npm:^8.4.23" + postcss-import: "npm:^15.1.0" + postcss-js: "npm:^4.0.1" + postcss-load-config: "npm:^4.0.1" + postcss-nested: "npm:^6.0.1" + postcss-selector-parser: "npm:^6.0.11" + postcss-value-parser: "npm:^4.2.0" + resolve: "npm:^1.22.2" + sucrase: "npm:^3.32.0" + bin: + tailwind: lib/cli.js + tailwindcss: lib/cli.js + checksum: 10c0/334e9828da03daaf9ebb3a539fd8b940b3625d60caf6e51e05400fb723ed4fbcb2fb2f0dc0012a10ad9d1469127ab543bbbf8a672f4932e27509ac39c312af28 + languageName: node + linkType: hard + "tailwindcss@npm:3.4.15": version: 3.4.15 resolution: "tailwindcss@npm:3.4.15" @@ -2984,6 +3451,17 @@ __metadata: languageName: node linkType: hard +"tw-to-css@npm:^0.0.12": + version: 0.0.12 + resolution: "tw-to-css@npm:0.0.12" + dependencies: + postcss: "npm:8.4.31" + postcss-css-variables: "npm:0.18.0" + tailwindcss: "npm:3.3.2" + checksum: 10c0/44436354b366c124b793aa30bb20002a6cc482aa30c4adc13cecdbb2bd1356fa9657ab929ce0cffe13cac67f8e31e73f480022fc41a4f57d383956f20075c986 + languageName: node + linkType: hard + "typescript@npm:5.6.3": version: 5.6.3 resolution: "typescript@npm:5.6.3" @@ -3140,6 +3618,13 @@ __metadata: languageName: node linkType: hard +"yoga-layout@npm:^3.2.1": + version: 3.2.1 + resolution: "yoga-layout@npm:3.2.1" + checksum: 10c0/9001e51be993c85e03757e5a04a2b61b8b30c9e5a7865d0156ca87a6431a3b717d51eb4990bfe588189fcfeac688dd9c3de707bbd50d1c344a84e63974cc54a8 + languageName: node + linkType: hard + "zod@npm:3.23.8": version: 3.23.8 resolution: "zod@npm:3.23.8" @@ -3147,6 +3632,13 @@ __metadata: languageName: node linkType: hard +"zod@npm:^3.22.4": + version: 3.24.2 + resolution: "zod@npm:3.24.2" + checksum: 10c0/c638c7220150847f13ad90635b3e7d0321b36cce36f3fc6050ed960689594c949c326dfe2c6fa87c14b126ee5d370ccdebd6efb304f41ef5557a4aaca2824565 + languageName: node + linkType: hard + "zustand@npm:5.0.1": version: 5.0.1 resolution: "zustand@npm:5.0.1" @@ -3168,7 +3660,7 @@ __metadata: languageName: node linkType: hard -"zustand@npm:^4.3.2": +"zustand@npm:^4.3.2, zustand@npm:^4.5.2": version: 4.5.6 resolution: "zustand@npm:4.5.6" dependencies: From 9136e8c373122c3f088671535ef393c8aaaee1ef Mon Sep 17 00:00:00 2001 From: Domenico Mancini Date: Fri, 28 Mar 2025 10:07:49 +0100 Subject: [PATCH 08/10] refactor(map): simplify flag style API --- dash/src/components/map/Map.tsx | 4 ++-- dash/src/components/map/Map3D.tsx | 6 +++--- dash/src/lib/getTrackStatusMessage.ts | 16 ---------------- 3 files changed, 5 insertions(+), 21 deletions(-) diff --git a/dash/src/components/map/Map.tsx b/dash/src/components/map/Map.tsx index 50b56f7a..570ee441 100644 --- a/dash/src/components/map/Map.tsx +++ b/dash/src/components/map/Map.tsx @@ -6,7 +6,7 @@ import type { PositionCar } from "@/types/state.type"; import { objectEntries } from "@/lib/driverHelper"; import { fetchMap } from "@/lib/fetchMap"; -import { getTrackColorStroke, getTrackStatusMessage } from "@/lib/getTrackStatusMessage"; +import { getComputedFlagStyle, getTrackStatusMessage } from "@/lib/getTrackStatusMessage"; import { createSectors, findYellowSectors, @@ -107,7 +107,7 @@ export default function Map() { return sectors .map((sector) => { const flagType = getSectorColor(sector, status?.bySector, status?.flagType, yellowSectors); - const stroke = getTrackColorStroke(flagType); + const { stroke } = getComputedFlagStyle(flagType); return { color: stroke, pulse: status?.pulse, diff --git a/dash/src/components/map/Map3D.tsx b/dash/src/components/map/Map3D.tsx index e9a4a506..51849ad1 100644 --- a/dash/src/components/map/Map3D.tsx +++ b/dash/src/components/map/Map3D.tsx @@ -4,7 +4,7 @@ import type { FlagType, TrackPosition } from "@/types/map.type"; import { objectEntries } from "@/lib/driverHelper"; import { fetchMap } from "@/lib/fetchMap"; -import { getTrackColorHex, getTrackStatusMessage } from "@/lib/getTrackStatusMessage"; +import { getComputedFlagStyle, getTrackStatusMessage } from "@/lib/getTrackStatusMessage"; import { createSectors, findYellowSectors, getSectorColor, MapSector, rad, rotate } from "@/lib/map"; import { toVector3 } from "@/lib/r3f"; import { useDataStore, usePositionStore } from "@/stores/useDataStore"; @@ -96,9 +96,9 @@ export default function Map3D() { return sectors .map((sector) => { const flagType = getSectorColor(sector, status?.bySector, status?.flagType, yellowSectors); - const hex = getTrackColorHex(flagType); + const { trackHex } = getComputedFlagStyle(flagType); return { - hex, + hex: trackHex, flagType: flagType, pulse: status?.pulse, number: sector.number, diff --git a/dash/src/lib/getTrackStatusMessage.ts b/dash/src/lib/getTrackStatusMessage.ts index 1cf627f5..7243ce83 100644 --- a/dash/src/lib/getTrackStatusMessage.ts +++ b/dash/src/lib/getTrackStatusMessage.ts @@ -43,22 +43,6 @@ export const getTrackStatusMessage = (statusCode: number | undefined): StatusMes return MESSAGE_MAP[statusCode] ?? MESSAGE_MAP[0]; }; -export function getTrackColorStroke(flag: FlagType) { - return STYLE_BY_FLAG_TYPE[flag].stroke; -} - -export function getTrackColorBg(flag: FlagType) { - return STYLE_BY_FLAG_TYPE[flag].bg; -} - -export function getTrackColorHex(flag: FlagType) { - return STYLE_BY_FLAG_TYPE[flag].trackHex; -} - -export function getTrackColorFlagHex(flag: FlagType) { - return STYLE_BY_FLAG_TYPE[flag].flagHex; -} - export function getComputedFlagStyle(flag: FlagType): ComputedFlagStyle { return { ...STYLE_BY_FLAG_TYPE[flag], From abe12e411abc105c26739e5ce5ea5df82378fbcd Mon Sep 17 00:00:00 2001 From: Domenico Mancini Date: Fri, 28 Mar 2025 10:53:50 +0100 Subject: [PATCH 09/10] feat(map3d): improve billboard styling --- dash/src/components/map/DriverIndicator.tsx | 67 ++++++++++++++++----- dash/src/components/map/Map3D.tsx | 1 + 2 files changed, 53 insertions(+), 15 deletions(-) diff --git a/dash/src/components/map/DriverIndicator.tsx b/dash/src/components/map/DriverIndicator.tsx index 6d8a86d7..058ceb3e 100644 --- a/dash/src/components/map/DriverIndicator.tsx +++ b/dash/src/components/map/DriverIndicator.tsx @@ -2,8 +2,9 @@ import { rotate } from "@/lib/map"; import { toVector3 } from "@/lib/r3f"; import { theme } from "@/styles/tailwindTheme"; import { PositionCar } from "@/types/state.type"; -import { Billboard, Sphere, Text as Text3D } from "@react-three/drei"; +import { Billboard, Line, Sphere } from "@react-three/drei"; import { useFrame } from "@react-three/fiber"; +import { Container, Root, Text as TextUI } from "@react-three/uikit"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Color, MeshBasicMaterial, Vector3 } from "three"; @@ -24,6 +25,7 @@ function calculateAverageUpdateTime(history: number[]) { type DriverIndicatorProps = { name: string; color: string | undefined; + racingNumber: string; favoriteDriver: boolean; pit: boolean; @@ -40,6 +42,7 @@ export function DriverIndicator({ pos, name, color, + racingNumber, favoriteDriver, pit, hidden, @@ -50,7 +53,7 @@ export function DriverIndicator({ const rotatedPos = rotate(pos.X, pos.Y, rotation, centerX, centerY); const opacity = useMemo(() => { - if (pit) return 0.2; + if (pit) return 0.3; return hidden ? 0 : 1; }, [pit, hidden]); @@ -62,22 +65,56 @@ export function DriverIndicator({ return m; }, [color, opacity]); + if (hidden) { + return null; + } + return ( - - {name} - + + + + + + {racingNumber} + + + + + {name} + + + + diff --git a/dash/src/components/map/Map3D.tsx b/dash/src/components/map/Map3D.tsx index 51849ad1..a6d21d8f 100644 --- a/dash/src/components/map/Map3D.tsx +++ b/dash/src/components/map/Map3D.tsx @@ -235,6 +235,7 @@ export default function Map3D() { rotation={rotation} centerX={centerX} centerY={centerY} + racingNumber={driver.racingNumber} /> ); })} From 108294dca54919cabc6d02e2cf15772d946bd929 Mon Sep 17 00:00:00 2001 From: Domenico Mancini Date: Fri, 28 Mar 2025 11:31:29 +0100 Subject: [PATCH 10/10] fix(map3d): improve favorite drivers style --- dash/src/components/map/DriverIndicator.tsx | 38 ++++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/dash/src/components/map/DriverIndicator.tsx b/dash/src/components/map/DriverIndicator.tsx index 058ceb3e..e0a35d64 100644 --- a/dash/src/components/map/DriverIndicator.tsx +++ b/dash/src/components/map/DriverIndicator.tsx @@ -65,6 +65,21 @@ export function DriverIndicator({ return m; }, [color, opacity]); + const styles = useMemo( + () => ({ + renderPriority: favoriteDriver ? 40 : 20, + billboardHeight: favoriteDriver ? 800 : 400, + billboardSize: { + x: favoriteDriver ? 1050 * 1.5 : 1050, + y: favoriteDriver ? 350 * 1.5 : 350, + }, + fontSize: favoriteDriver ? 24000 * 1.5 : 24000, + fontWeight: favoriteDriver ? 600 : 500, + dotSize: favoriteDriver ? 180 : 120, + }), + [favoriteDriver], + ); + if (hidden) { return null; } @@ -75,14 +90,19 @@ export function DriverIndicator({ - - + + {racingNumber} @@ -105,10 +125,10 @@ export function DriverIndicator({ {name} @@ -116,7 +136,7 @@ export function DriverIndicator({ - + ); }