diff --git a/package-lock.json b/package-lock.json index 51a6d303..fc1bb06b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,9 +16,9 @@ "fflate": "^0.7.0", "object-path": "^0.11.4", "prop-types": "^15.6.2", - "react": "^16.5.2", + "react": "^16.14.0", "react-dat-gui": "^4.0.0", - "react-dom": "^16.5.2", + "react-dom": "^16.14.0", "terser": "^5.14.2", "three": "^0.147.0", "three-line-2d": "^1.1.6", @@ -5699,6 +5699,338 @@ "@types/node": ">= 8" } }, + "node_modules/@parcel/watcher": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz", + "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.4.1", + "@parcel/watcher-darwin-arm64": "2.4.1", + "@parcel/watcher-darwin-x64": "2.4.1", + "@parcel/watcher-freebsd-x64": "2.4.1", + "@parcel/watcher-linux-arm-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-musl": "2.4.1", + "@parcel/watcher-linux-x64-glibc": "2.4.1", + "@parcel/watcher-linux-x64-musl": "2.4.1", + "@parcel/watcher-win32-arm64": "2.4.1", + "@parcel/watcher-win32-ia32": "2.4.1", + "@parcel/watcher-win32-x64": "2.4.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz", + "integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz", + "integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz", + "integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz", + "integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz", + "integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz", + "integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz", + "integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz", + "integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz", + "integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz", + "integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz", + "integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz", + "integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher/node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@parcel/watcher/node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@parcel/watcher/node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/watcher/node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@parcel/watcher/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/@parcel/watcher/node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/@parcel/watcher/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/@sinonjs/commons": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.2.tgz", @@ -8908,6 +9240,17 @@ "node": ">=4" } }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -10371,6 +10714,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -11626,9 +11970,9 @@ } }, "node_modules/immutable": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz", - "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==" + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==" }, "node_modules/import-cwd": { "version": "3.0.0", @@ -15942,6 +16286,11 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==" + }, "node_modules/node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -21026,11 +21375,12 @@ } }, "node_modules/sass": { - "version": "1.69.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.69.0.tgz", - "integrity": "sha512-l3bbFpfTOGgQZCLU/gvm1lbsQ5mC/WnLz3djL2v4WCJBDrWm58PO+jgngcGRNnKUh6wSsdm50YaovTqskZ0xDQ==", + "version": "1.79.5", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.79.5.tgz", + "integrity": "sha512-W1h5kp6bdhqFh2tk3DsI771MoEJjvrSY/2ihJRJS4pjIyfJCw0nTsxqhnrUzaLMOJjFchj8rOvraI/YUVjtx5g==", "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", + "@parcel/watcher": "^2.4.1", + "chokidar": "^4.0.0", "immutable": "^4.0.0", "source-map-js": ">=0.6.2 <2.0.0" }, @@ -21041,151 +21391,30 @@ "node": ">=14.0.0" } }, - "node_modules/sass/node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/sass/node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/sass/node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/sass/node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/sass/node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/sass/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", "dependencies": { - "is-glob": "^4.0.1" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/sass/node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dependencies": { - "binary-extensions": "^2.0.0" + "node": ">= 14.16.0" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/sass/node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sass/node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sass/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/sass/node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://paulmillr.com/funding/" } }, "node_modules/sass/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dependencies": { - "picomatch": "^2.2.1" - }, + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/sass/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "^7.0.0" + "node": ">= 14.16.0" }, - "engines": { - "node": ">=8.0" + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/sax": { @@ -21611,9 +21840,9 @@ } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "engines": { "node": ">=0.10.0" } @@ -28015,6 +28244,154 @@ "@types/node": ">= 8" } }, + "@parcel/watcher": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz", + "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", + "requires": { + "@parcel/watcher-android-arm64": "2.4.1", + "@parcel/watcher-darwin-arm64": "2.4.1", + "@parcel/watcher-darwin-x64": "2.4.1", + "@parcel/watcher-freebsd-x64": "2.4.1", + "@parcel/watcher-linux-arm-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-musl": "2.4.1", + "@parcel/watcher-linux-x64-glibc": "2.4.1", + "@parcel/watcher-linux-x64-musl": "2.4.1", + "@parcel/watcher-win32-arm64": "2.4.1", + "@parcel/watcher-win32-ia32": "2.4.1", + "@parcel/watcher-win32-x64": "2.4.1", + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "dependencies": { + "braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "requires": { + "fill-range": "^7.1.1" + } + }, + "fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "requires": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "@parcel/watcher-android-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz", + "integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==", + "optional": true + }, + "@parcel/watcher-darwin-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz", + "integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==", + "optional": true + }, + "@parcel/watcher-darwin-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz", + "integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==", + "optional": true + }, + "@parcel/watcher-freebsd-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz", + "integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==", + "optional": true + }, + "@parcel/watcher-linux-arm-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz", + "integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==", + "optional": true + }, + "@parcel/watcher-linux-arm64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz", + "integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==", + "optional": true + }, + "@parcel/watcher-linux-arm64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz", + "integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==", + "optional": true + }, + "@parcel/watcher-linux-x64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz", + "integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==", + "optional": true + }, + "@parcel/watcher-linux-x64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz", + "integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==", + "optional": true + }, + "@parcel/watcher-win32-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz", + "integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==", + "optional": true + }, + "@parcel/watcher-win32-ia32": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz", + "integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==", + "optional": true + }, + "@parcel/watcher-win32-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz", + "integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==", + "optional": true + }, "@sinonjs/commons": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.2.tgz", @@ -30575,6 +30952,11 @@ "integrity": "sha1-OHHMCmoALow+Wzz38zYmRnXwa50=", "dev": true }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==" + }, "detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -31685,6 +32067,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, "optional": true }, "function-bind": { @@ -32638,9 +33021,9 @@ } }, "immutable": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz", - "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==" + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==" }, "import-cwd": { "version": "3.0.0", @@ -35990,6 +36373,11 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==" + }, "node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -39927,114 +40315,28 @@ } }, "sass": { - "version": "1.69.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.69.0.tgz", - "integrity": "sha512-l3bbFpfTOGgQZCLU/gvm1lbsQ5mC/WnLz3djL2v4WCJBDrWm58PO+jgngcGRNnKUh6wSsdm50YaovTqskZ0xDQ==", + "version": "1.79.5", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.79.5.tgz", + "integrity": "sha512-W1h5kp6bdhqFh2tk3DsI771MoEJjvrSY/2ihJRJS4pjIyfJCw0nTsxqhnrUzaLMOJjFchj8rOvraI/YUVjtx5g==", "requires": { - "chokidar": ">=3.0.0 <4.0.0", + "@parcel/watcher": "^2.4.1", + "chokidar": "^4.0.0", "immutable": "^4.0.0", "source-map-js": ">=0.6.2 <2.0.0" }, "dependencies": { - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "requires": { - "fill-range": "^7.0.1" - } - }, "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "requires": { - "is-glob": "^4.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", "requires": { - "is-extglob": "^2.1.1" + "readdirp": "^4.0.1" } }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" - }, "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "requires": { - "picomatch": "^2.2.1" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "requires": { - "is-number": "^7.0.0" - } + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==" } } }, @@ -40376,9 +40678,9 @@ "dev": true }, "source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" }, "source-map-resolve": { "version": "0.5.2", diff --git a/packages/troika-3d-text/src/facade/BatchedText3DFacade.js b/packages/troika-3d-text/src/facade/BatchedText3DFacade.js new file mode 100644 index 00000000..7057d92f --- /dev/null +++ b/packages/troika-3d-text/src/facade/BatchedText3DFacade.js @@ -0,0 +1,26 @@ +import { Object3DFacade } from "troika-3d"; +import { BatchedText } from "troika-three-text"; +import { TEXT_MESH_PROPS } from "./Text3DFacade"; + +export class BatchedText3DFacade extends Object3DFacade { + constructor(parent) { + super(parent, new BatchedText()) + + // Orphaned container for children so they don't end up in the scene + this._texts = new Object3DFacade() + } + + afterUpdate() { + TEXT_MESH_PROPS.forEach(prop => { + this.threeObject[prop] = this[prop]; + }) + this._texts.children = this.children + this._texts.afterUpdate() + this._texts.forEachChild(text => this.threeObject.addText(text.threeObject)) + super.afterUpdate() + } + + shouldUpdateChildren() { + return false + } +} diff --git a/packages/troika-3d-text/src/facade/Text3DFacade.js b/packages/troika-3d-text/src/facade/Text3DFacade.js index 29b6734e..ad9e8a31 100644 --- a/packages/troika-3d-text/src/facade/Text3DFacade.js +++ b/packages/troika-3d-text/src/facade/Text3DFacade.js @@ -3,7 +3,7 @@ import { Text } from 'troika-three-text' import SelectionManagerFacade from './SelectionManagerFacade.js' // Properties that will simply be forwarded to the TextMesh: -const TEXT_MESH_PROPS = [ +export const TEXT_MESH_PROPS = [ 'text', 'anchorX', 'anchorY', diff --git a/packages/troika-3d-text/src/index.js b/packages/troika-3d-text/src/index.js index 81935151..dfdcdf28 100644 --- a/packages/troika-3d-text/src/index.js +++ b/packages/troika-3d-text/src/index.js @@ -12,3 +12,4 @@ export { // Troika framework specific exports: export {default as Text3DFacade} from './facade/Text3DFacade.js' +export { BatchedText3DFacade } from './facade/BatchedText3DFacade.js' diff --git a/packages/troika-examples/index.js b/packages/troika-examples/index.js index 0cf77cec..321d384f 100644 --- a/packages/troika-examples/index.js +++ b/packages/troika-examples/index.js @@ -8,6 +8,7 @@ import GlobeExample from './globe/GlobeExample' import GlobeConnectionsExample from './globe-connections/GlobeConnectionsExample' import HtmlOverlays from './html-overlays/HtmlOverlaysExample' import TextExample from './text/TextExample' +import BatchedTextExample from "./text-batched/BatchedTextExample"; import FlexboxExample from './flexbox/FlexboxExample' import UIExample from './ui2/UIExample' import DragDrop from './dragdrop/DragDropExample' @@ -32,6 +33,7 @@ const EXAMPLES = [ {id: 'globeConnections', name: 'Globe Connections', component: GlobeConnectionsExample}, {id: 'htmlOverlays', name: 'HTML Overlays', component: HtmlOverlays}, {id: 'text', name: '3D Text', component: TextExample}, + {id: 'batchedText', name: '3D Text - Batched', component: BatchedTextExample}, {id: 'flexbox', name: 'Flexbox UI Layout', component: FlexboxExample}, {id: 'ui', name: 'User Interface', component: UIExample}, {id: 'dragdrop', name: 'Drag and Drop', component: DragDrop}, diff --git a/packages/troika-examples/package.json b/packages/troika-examples/package.json index 4bb9b123..69da46f5 100644 --- a/packages/troika-examples/package.json +++ b/packages/troika-examples/package.json @@ -18,9 +18,9 @@ "adaptive-bezier-curve": "^1.0.3", "d3-hierarchy": "^1.1.8", "d3-voronoi": "^1.1.4", - "react": "^16.5.2", + "react": "^16.14.0", "react-dat-gui": "^4.0.0", - "react-dom": "^16.5.2", + "react-dom": "^16.14.0", "three": "^0.147.0", "three-instanced-uniforms-mesh": "^0.49.1", "three-line-2d": "^1.1.6", diff --git a/packages/troika-examples/text-batched/BatchedTextExample.jsx b/packages/troika-examples/text-batched/BatchedTextExample.jsx new file mode 100644 index 00000000..1cc16593 --- /dev/null +++ b/packages/troika-examples/text-batched/BatchedTextExample.jsx @@ -0,0 +1,79 @@ +import React from "react"; +import { Canvas3D } from "troika-3d"; +import { Text3DFacade, BatchedText3DFacade } from "troika-3d-text"; +import { FONTS } from '../text/TextExample' +import { Color } from "three/src/math/Color"; + +export default function BatchedTextExample ({ stats, width, height }) { + const [texts, setTexts] = React.useState(randomizeText()); + + function randomizeText() { + const all = [ + "One", "Two", "Three", "Four", "Five", + "Six", "Seven", "Eight", "Nine", "Ten", + "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", + "Sixteen", "Seventeen", "Eighteen", "Nineteen", "Twenty" + ] + const subset = all.slice(0, Math.max(8, Math.floor(Math.random() * all.length))) + return subset.map((text, i) => ({ + facade: Text3DFacade, + text, + font: Object.values(FONTS)[i % Object.values(FONTS).length], + fontSize: randRange(0.05, 0.25), + x: randRange(-1, 1), + y: randRange(-1, 1), + z: randRange(-1, 1), + rotateZ: randRange(-0.01, 0.01), + anchorX: "50%", + anchorY: "50%", + color: randColor(), + animation: { + from: { rotateY: 0 }, + to: { rotateY: Math.PI * 2 }, + duration: randRange(800, 3000), + iterations: Infinity + } + })) + } + + return
+ { + setTexts(randomizeText()) + }} + camera={{ + fov: 75, + aspect: width / height, + x: 0, + y: 0, + z: 2.5 + }} + objects={[ + { + facade: BatchedText3DFacade, + children: texts, + animation: { + from: { rotateY: 0 }, + to: { rotateY: Math.PI * 2 }, + duration: 10000, + iterations: Infinity + } + } + ]} + /> +
; +} + +function randRange (min, max) { + return min + Math.random() * (max - min); +} +function randColor() { + return new Color().setHSL(Math.random(), 1, 0.5) +} +// function randFromArray(array) { +// return array[Math.floor(Math.random() * array.length)]; +// } diff --git a/packages/troika-examples/text/TextExample.jsx b/packages/troika-examples/text/TextExample.jsx index 524da8a4..5e2dadbf 100644 --- a/packages/troika-examples/text/TextExample.jsx +++ b/packages/troika-examples/text/TextExample.jsx @@ -16,7 +16,7 @@ import { DatGuiFacade } from 'troika-3d-ui' import { ExampleConfigurator } from '../_shared/ExampleConfigurator.js' -const FONTS = { +export const FONTS = { 'Noto Sans (none)': null, 'Roboto': 'https://fonts.gstatic.com/s/roboto/v18/KFOmCnqEu92Fr1Mu4mxM.woff', 'Alex Brush': 'https://fonts.gstatic.com/s/alexbrush/v8/SZc83FzrJKuqFbwMKk6EhUXz6w.woff', diff --git a/packages/troika-three-text/src/BatchedText.js b/packages/troika-three-text/src/BatchedText.js new file mode 100644 index 00000000..31ff018c --- /dev/null +++ b/packages/troika-three-text/src/BatchedText.js @@ -0,0 +1,334 @@ +import { Text } from "./Text.js"; +import { DataTexture, FloatType, RGBAFormat, Vector2, Box3, Color } from "three"; +import { glyphBoundsAttrName, glyphIndexAttrName } from "./GlyphsGeometry"; +import { createDerivedMaterial } from "troika-three-utils"; +import { createTextDerivedMaterial } from "./TextDerivedMaterial"; + +const syncStartEvent = { type: "syncstart" }; +const syncCompleteEvent = { type: "synccomplete" }; +const instanceIndexAttrName = "aTroikaTextInstanceIndex"; + +// 0-15: matrix +// 16: color +let floatsPerInstance = 17; +floatsPerInstance = Math.ceil(floatsPerInstance / 4) * 4; // whole texels + +const tempBox3 = new Box3(); +const tempColor = new Color(); + +/** + * @experimental + * + * A specialized `Text` implementation that accepts any number of `Text` children + * and automatically batches them together to render in a single draw call. + * + * The `material` of each child `Text` will be ignored, and the `material` of the + * `BatchedText` will be used for all of them instead. + * + * @todo Support for WebGL1 without OES_texture_float extension (pack floats into 4 ints) + * @todo Handle more visual uniforms: uv bounds, outlines, etc. + * @todo Handle things that can't vary between instances like sdfGlyphSize - separate batches, or throw? + */ +export class BatchedText extends Text { + constructor () { + super(); + + /** + * @typedef {Object} PackingInfo + * @property {number} index - the packing order index when last packed, or -1 + * @property {boolean} dirty - whether it has synced since last pack + */ + + /** + * @type {Map} + */ + this._texts = new Map(); + + this._onInstanceSynced = (e) => { + this._texts.get(e.target).dirty = true; + }; + this._onInstanceRemoved = (e) => { + this.removeText(e.target); + }; + } + + /** + * @override + * Batch any Text instances added as children + */ + add (...objects) { + for (let i = 0; i < objects.length; i++) { + if (objects[i] instanceof Text) { + this.addText(objects[i]); + } else { + super.add(objects[i]); + } + } + return this; + } + + /** + * @param {Text} text + */ + addText (text) { + if (!this._texts.has(text)) { + this._texts.set(text, { + index: -1, + glyphCount: -1, + dirty: true + }); + text.addEventListener("removed", this._onInstanceRemoved); + text.addEventListener("synccomplete", this._onInstanceSynced); + } + } + + /** + * @param {Text} text + */ + removeText (text) { + text.removeEventListener("removed", this._onInstanceRemoved); + text.removeEventListener("synccomplete", this._onInstanceSynced); + this._texts.delete(text); + } + + /** + * Use the custom derivation with extra batching logic + */ + createDerivedMaterial (baseMaterial) { + return createBatchedTextMaterial(baseMaterial); + } + + updateMatrixWorld (force) { + super.updateMatrixWorld(force); + this.updateBounds(); + } + + /** + * Update the batched geometry bounds to hold all instances + */ + updateBounds () { + // Update instance local matrices and the overall bounds + const bbox = this.geometry.boundingBox.makeEmpty(); + this._texts.forEach((_, text) => { + if (text.matrixAutoUpdate) text.updateMatrix(); // ignore world matrix + tempBox3.copy(text.geometry.boundingBox).applyMatrix4(text.matrix); + bbox.union(tempBox3); + }); + bbox.getBoundingSphere(this.geometry.boundingSphere); + } + + /** + * @override + */ + _prepareForRender (material) { + // Copy instance matrices to the texture + // TODO only do this once, not once per material + + // Resize the texture to fit in powers of 2 + let texture = this._mat4Texture; + const dataLength = Math.pow(2, Math.ceil(Math.log2(this._texts.size * floatsPerInstance))); + if (!texture || dataLength !== texture.image.data.length) { + // console.log(`resizing: ${dataLength}`); + if (texture) texture.dispose(); + const width = Math.min(dataLength / 4, 1024); + texture = this._mat4Texture = new DataTexture( + new Float32Array(dataLength), + width, + dataLength / 4 / width, + RGBAFormat, + FloatType + ); + } + + const texData = texture.image.data; + const setTexData = (index, value) => { + if (value !== texData[index]) { + texData[index] = value; + texture.needsUpdate = true; + } + } + this._texts.forEach(({ index, dirty }, text) => { + if (index > -1) { + const startIndex = index * floatsPerInstance + + // Matrix + const matrix = text.matrix.elements; + for (let i = 0; i < 16; i++) { + setTexData(startIndex + i, matrix[i]) + } + + // Color + let color = text.color; + if (color == null) color = this.color; + if (color == null) color = this._baseMaterial.color; + if (color == null) color = 0xffffff; + setTexData(startIndex + 16, tempColor.set(color).getHex()); + + // TODO: + // outlineWidth/Color/Opacity/Blur/Offset + // strokeWidth/Color/Opacity + // fillOpacity + // clipRect + } + }); + material.setMatrixTexture(texture); + + super._prepareForRender(material); + } + + sync (callback) { + // TODO: skip instances updating their geometries, just use textRenderInfo directly + + // Trigger sync on all instances that need it + let syncPromises; + this._texts.forEach((packingInfo, text) => { + if (packingInfo.dirty || text._needsSync) { + packingInfo.dirty = false; + (syncPromises || (syncPromises = [])).push(new Promise(resolve => { + if (text._needsSync) { + text.sync(resolve); + } else { + resolve(); + } + })); + } + }); + + // If any needed syncing, wait for them and then repack the batched geometry + if (syncPromises) { + this.dispatchEvent(syncStartEvent); + + Promise.all(syncPromises).then(() => { + const { geometry } = this; + const batchedAttributes = geometry.attributes; + let instanceIndexes = batchedAttributes[instanceIndexAttrName] && batchedAttributes[instanceIndexAttrName].array || new Uint8Array(0); + let batchedGlyphIndexes = batchedAttributes[glyphIndexAttrName] && batchedAttributes[glyphIndexAttrName].array || new Float32Array(0); + let batchedGlyphBounds = batchedAttributes[glyphBoundsAttrName] && batchedAttributes[glyphBoundsAttrName].array || new Float32Array(0); + + // Initial pass to collect total glyph count and resize the arrays if needed + let totalGlyphCount = 0; + this._texts.forEach((packingInfo, { textRenderInfo }) => { + if (textRenderInfo) { + totalGlyphCount += textRenderInfo.glyphAtlasIndices.length; + this._textRenderInfo = textRenderInfo; // TODO - need this, but be smarter + } + }); + if (totalGlyphCount !== instanceIndexes.length) { + instanceIndexes = cloneAndResize(instanceIndexes, totalGlyphCount); + batchedGlyphIndexes = cloneAndResize(batchedGlyphIndexes, totalGlyphCount); + batchedGlyphBounds = cloneAndResize(batchedGlyphBounds, totalGlyphCount * 4); + } + + // Populate batch arrays + let instanceIndex = 0; + let glyphIndex = 0; + this._texts.forEach((packingInfo, { textRenderInfo }) => { + if (textRenderInfo) { + const glyphCount = textRenderInfo.glyphAtlasIndices.length; + instanceIndexes.fill(instanceIndex, glyphIndex, glyphIndex + glyphCount); + + // TODO can skip these for instances that are not dirty or shifting overall position: + batchedGlyphIndexes.set(textRenderInfo.glyphAtlasIndices, glyphIndex, glyphIndex + glyphCount); + batchedGlyphBounds.set(textRenderInfo.glyphBounds, glyphIndex * 4, (glyphIndex + glyphCount) * 4); + + glyphIndex += glyphCount; + packingInfo.index = instanceIndex++; + } + }); + + // Update the geometry attributes + geometry.updateAttributeData(instanceIndexAttrName, instanceIndexes, 1); + geometry.updateAttributeData(glyphIndexAttrName, batchedGlyphIndexes, 1); + geometry.updateAttributeData(glyphBoundsAttrName, batchedGlyphBounds, 4); + + this.updateBounds(); + + this.dispatchEvent(syncCompleteEvent); + if (callback) { + callback(); + } + }); + } + } + + copy (source) { + if (source instanceof BatchedText) { + super.copy(source); + this._texts.forEach((_, text) => this.removeText(text)); + source._texts.forEach((_, text) => this.addText(text)); + } + return this; + } + + dispose () { + super.dispose(); + this._mat4Texture.dispose(); + } +} + +function cloneAndResize (source, newLength) { + const copy = new source.constructor(newLength); + copy.set(source.subarray(0, newLength)); + return copy; +} + +function createBatchedTextMaterial (baseMaterial) { + const texUniformName = "uTroikaMatricesTexture"; + const texSizeUniformName = "uTroikaMatricesTextureSize"; + const colorVaryingName = "vTroikaTextColor"; + const batchMaterial = createDerivedMaterial(baseMaterial, { + chained: true, + uniforms: { + [texSizeUniformName]: { value: new Vector2() }, + [texUniformName]: { value: null } + }, + // language=GLSL + vertexDefs: ` + uniform sampler2D ${texUniformName}; + uniform vec2 ${texSizeUniformName}; + attribute float ${instanceIndexAttrName}; + varying vec3 ${colorVaryingName}; + + vec4 troikaGetTexel(float i) { + float w = ${texSizeUniformName}.x; + vec2 uv = (vec2(mod(i, w), floor(i / w)) + 0.5) / w; + return texture2D(${texUniformName}, uv); + } + vec3 troikaFloatToColor(float v) { + return mod(floor(vec3(v / 65536.0, v / 256.0, v)), 256.0) / 256.0; + } + `, + // language=GLSL prefix="void main() {" suffix="}" + vertexTransform: ` + float i = ${instanceIndexAttrName} * ${floatsPerInstance.toFixed(1)} / 4.0; + mat4 matrix = mat4( + troikaGetTexel(i), + troikaGetTexel(i + 1.0), + troikaGetTexel(i + 2.0), + troikaGetTexel(i + 3.0) + ); + position.xyz = (matrix * vec4(position, 1.0)).xyz; + + float packedColor = troikaGetTexel(i + 4.0).r; + ${colorVaryingName} = troikaFloatToColor(packedColor); + `, + // language=GLSL + fragmentDefs: ` + varying vec3 ${colorVaryingName}; + `, + // language=GLSL prefix="void main() {" suffix="}" + fragmentColorTransform: ` + gl_FragColor.rgb = ${colorVaryingName}; + ` + // TODO: If the base shader has a diffuse color modify that rather than gl_FragColor + // customRewriter({vertexShader, fragmentShader}) { + // return {vertexShader, fragmentShader} + // }, + }); + batchMaterial.setMatrixTexture = (texture) => { + batchMaterial.uniforms[texUniformName].value = texture; + batchMaterial.uniforms[texSizeUniformName].value.set(texture.image.width, texture.image.height); + }; + return createTextDerivedMaterial(batchMaterial); +} + diff --git a/packages/troika-three-text/src/GlyphsGeometry.js b/packages/troika-three-text/src/GlyphsGeometry.js index 4dbaa22a..3e5f7c17 100644 --- a/packages/troika-three-text/src/GlyphsGeometry.js +++ b/packages/troika-three-text/src/GlyphsGeometry.js @@ -151,9 +151,9 @@ class GlyphsGeometry extends InstancedBufferGeometry { */ updateGlyphs(glyphBounds, glyphAtlasIndices, blockBounds, chunkedBounds, glyphColors) { // Update the instance attributes - updateBufferAttr(this, glyphBoundsAttrName, glyphBounds, 4) - updateBufferAttr(this, glyphIndexAttrName, glyphAtlasIndices, 1) - updateBufferAttr(this, glyphColorAttrName, glyphColors, 3) + this.updateAttributeData(glyphBoundsAttrName, glyphBounds, 4) + this.updateAttributeData(glyphIndexAttrName, glyphAtlasIndices, 1) + this.updateAttributeData(glyphColorAttrName, glyphColors, 3) this._blockBounds = blockBounds this._chunkedBounds = chunkedBounds this.instanceCount = glyphAtlasIndices.length @@ -215,32 +215,37 @@ class GlyphsGeometry extends InstancedBufferGeometry { } this.instanceCount = count } -} - -function updateBufferAttr(geom, attrName, newArray, itemSize) { - const attr = geom.getAttribute(attrName) - if (newArray) { - // If length isn't changing, just update the attribute's array data - if (attr && attr.array.length === newArray.length) { - attr.array.set(newArray) - attr.needsUpdate = true - } else { - geom.setAttribute(attrName, new InstancedBufferAttribute(newArray, itemSize)) - // If the new attribute has a different size, we also have to (as of r117) manually clear the - // internal cached max instance count. See https://github.com/mrdoob/three.js/issues/19706 - // It's unclear if this is a threejs bug or a truly unsupported scenario; discussion in - // that ticket is ambiguous as to whether replacing a BufferAttribute with one of a - // different size is supported, but https://github.com/mrdoob/three.js/pull/17418 strongly - // implies it should be supported. It's possible we need to - delete geom._maxInstanceCount //for r117+, could be fragile - geom.dispose() //for r118+, more robust feeling, but more heavy-handed than I'd like + /** + * Utility for updating instance attributes with automatic resizing + */ + updateAttributeData(attrName, newArray, itemSize) { + const attr = this.getAttribute(attrName) + if (newArray) { + // If length isn't changing, just update the attribute's array data + if (attr && attr.array.length === newArray.length) { + attr.array.set(newArray) + attr.needsUpdate = true + } else { + this.setAttribute(attrName, new InstancedBufferAttribute(newArray, itemSize)) + // If the new attribute has a different size, we also have to (as of r117) manually clear the + // internal cached max instance count. See https://github.com/mrdoob/three.js/issues/19706 + // It's unclear if this is a threejs bug or a truly unsupported scenario; discussion in + // that ticket is ambiguous as to whether replacing a BufferAttribute with one of a + // different size is supported, but https://github.com/mrdoob/three.js/pull/17418 strongly + // implies it should be supported. It's possible we need to + delete this._maxInstanceCount //for r117+, could be fragile + this.dispose() //for r118+, more robust feeling, but more heavy-handed than I'd like + } + } else if (attr) { + this.deleteAttribute(attrName) } - } else if (attr) { - geom.deleteAttribute(attrName) } } export { - GlyphsGeometry + GlyphsGeometry, + glyphBoundsAttrName, + glyphColorAttrName, + glyphIndexAttrName, } diff --git a/packages/troika-three-text/src/index.js b/packages/troika-three-text/src/index.js index d4668f2d..c7ba46e4 100644 --- a/packages/troika-three-text/src/index.js +++ b/packages/troika-three-text/src/index.js @@ -3,6 +3,7 @@ export { configureTextBuilder, getTextRenderInfo, typesetterWorkerModule, preloadFont, dumpSDFTextures } from './TextBuilder.js' export { fontResolverWorkerModule } from './FontResolver.js' export { Text } from './Text.js' +export { BatchedText } from './BatchedText.js' export { GlyphsGeometry } from './GlyphsGeometry.js' export { createTextDerivedMaterial } from './TextDerivedMaterial.js' export { getCaretAtPoint, getSelectionRects } from './selectionUtils.js'