├── examples
├── r3f-zustand
│ ├── src
│ │ ├── vite-env.d.ts
│ │ ├── index.css
│ │ ├── main.tsx
│ │ ├── Controls
│ │ │ ├── TEMPORARY_control-kit
│ │ │ │ ├── index.ts
│ │ │ │ ├── ControlRig.tsx
│ │ │ │ ├── dom.ts
│ │ │ │ ├── camera.ts
│ │ │ │ └── SphericalCamera.tsx
│ │ │ ├── cameraState.ts
│ │ │ ├── index.tsx
│ │ │ └── eventHandler.ts
│ │ ├── CameraHints.tsx
│ │ └── App.tsx
│ ├── vite.config.ts
│ ├── tsconfig.node.json
│ ├── .gitignore
│ ├── index.html
│ ├── tsconfig.json
│ ├── package.json
│ ├── README.md
│ └── package-lock.json
├── EXAMPLE-React-props-useState.md
└── EXAMPLE-WIP-multitouch-gestures.md
├── src
├── index.ts
├── ControlRig.tsx
├── dom.ts
├── camera.ts
└── SphericalCamera.tsx
├── .gitignore
├── tsconfig.json
├── package.json
├── README.md
└── camera-state.svg
/examples/r3f-zustand/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/examples/r3f-zustand/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | overflow: hidden;
5 | }
6 |
--------------------------------------------------------------------------------
/examples/r3f-zustand/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react-swc'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/examples/r3f-zustand/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | // DOM API
2 | export { bitmaskToArray, addEventListeners, addPreventDefaults } from './dom'
3 | export { ControlRig } from './ControlRig'
4 |
5 | // Camera API
6 | export { normalizeCoords, getScreenXY } from './camera'
7 | export { SphericalCamera } from './SphericalCamera'
8 |
--------------------------------------------------------------------------------
/examples/r3f-zustand/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
7 |
8 |
9 |
10 | )
11 |
--------------------------------------------------------------------------------
/examples/r3f-zustand/src/Controls/TEMPORARY_control-kit/index.ts:
--------------------------------------------------------------------------------
1 | // DOM API
2 | export { bitmaskToArray, addEventListeners, addPreventDefaults } from './dom'
3 | export { ControlRig } from './ControlRig'
4 |
5 | // Camera API
6 | export { normalizeCoords, getScreenXY } from './camera'
7 | export { SphericalCamera } from './SphericalCamera'
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist-ssr
12 | *.local
13 |
14 | # Editor directories and files
15 | .vscode/*
16 | !.vscode/extensions.json
17 | .idea
18 | .DS_Store
19 | *.suo
20 | *.ntvs*
21 | *.njsproj
22 | *.sln
23 | *.sw?
24 |
--------------------------------------------------------------------------------
/examples/r3f-zustand/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/examples/r3f-zustand/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2018",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "outDir": "./dist",
6 | "declaration": true,
7 | "module": "esnext",
8 | "allowJs": true,
9 | "skipLibCheck": true,
10 | "esModuleInterop": true,
11 | "allowSyntheticDefaultImports": true,
12 | "strict": true,
13 | "forceConsistentCasingInFileNames": true,
14 | "noFallthroughCasesInSwitch": true,
15 | "moduleResolution": "node",
16 | "resolveJsonModule": true,
17 | "isolatedModules": true,
18 | "jsx": "react-jsx"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/examples/r3f-zustand/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": true,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx"
18 | },
19 | "include": ["src"],
20 | "references": [{ "path": "./tsconfig.node.json" }]
21 | }
22 |
--------------------------------------------------------------------------------
/examples/r3f-zustand/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-r3f",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc && vite build",
9 | "preview": "vite preview",
10 | "format": "prettier src --write",
11 | "start": "npm run dev"
12 | },
13 | "dependencies": {
14 | "@react-three/drei": "^9.66.1",
15 | "@react-three/fiber": "^8.13.0",
16 | "control-kit": "file:../",
17 | "react": "^18.2.0",
18 | "react-dom": "^18.2.0",
19 | "three": "^0.152.2",
20 | "zustand": "^4.3.7"
21 | },
22 | "devDependencies": {
23 | "@types/react": "^18.0.28",
24 | "@types/react-dom": "^18.0.11",
25 | "@vitejs/plugin-react-swc": "^3.0.0",
26 | "typescript": "^4.9.3",
27 | "vite": "^4.2.0"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/ControlRig.tsx:
--------------------------------------------------------------------------------
1 | import { memo, useEffect } from 'react'
2 | import { addEventListeners, addPreventDefaults } from './dom'
3 |
4 | interface ControlRigProps {
5 | target?: HTMLElement
6 | onEvent: any
7 | options?: boolean | AddEventListenerOptions
8 | preventDefaults?: string[]
9 | eventTypes?: string[]
10 | }
11 |
12 | export const ControlRig = memo(
13 | ({
14 | target,
15 | onEvent,
16 | options,
17 | preventDefaults = [],
18 | eventTypes = []
19 | }: ControlRigProps) => {
20 | useEffect(
21 | () => addPreventDefaults(target, preventDefaults),
22 | [target, preventDefaults]
23 | )
24 | useEffect(
25 | () => addEventListeners(target, eventTypes, onEvent, options),
26 | [target, eventTypes, onEvent, options]
27 | )
28 | return null
29 | }
30 | )
31 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "control-kit",
3 | "version": "0.0.0",
4 | "type": "module",
5 | "main": "src/index.js",
6 | "peerDependencies": {
7 | "react": ">=18.0"
8 | },
9 | "scripts": {
10 | "build": "tsc",
11 | "test": "tsc --noEmit && echo 'no tests'",
12 | "dev": "tsc --watch --pretty",
13 | "format": "prettier src --write",
14 | "start": "cd examples/r3f-zustand && npm install && npm run dev"
15 | },
16 | "dependencies": {
17 | "@react-three/drei": ">=9.53",
18 | "three": ">=0.149"
19 | },
20 | "devDependencies": {
21 | "@types/node": "^18.11.19",
22 | "@types/react": "^18.2.0",
23 | "@types/react-dom": "^18.2.0",
24 | "@types/three": "^0.149.0",
25 | "prettier-config-standard": "^5.0.0",
26 | "typescript": "^5.0.0"
27 | },
28 | "prettier": "prettier-config-standard"
29 | }
30 |
--------------------------------------------------------------------------------
/examples/r3f-zustand/src/Controls/TEMPORARY_control-kit/ControlRig.tsx:
--------------------------------------------------------------------------------
1 | import { memo, useEffect } from 'react'
2 | import { addEventListeners, addPreventDefaults } from './dom'
3 |
4 | interface ControlRigProps {
5 | target?: HTMLElement
6 | onEvent: any
7 | options?: boolean | AddEventListenerOptions
8 | preventDefaults?: string[]
9 | eventTypes?: string[]
10 | }
11 |
12 | export const ControlRig = memo(
13 | ({
14 | target,
15 | onEvent,
16 | options,
17 | preventDefaults = [],
18 | eventTypes = []
19 | }: ControlRigProps) => {
20 | useEffect(
21 | () => addPreventDefaults(target, preventDefaults),
22 | [target, preventDefaults]
23 | )
24 | useEffect(
25 | () => addEventListeners(target, eventTypes, onEvent, options),
26 | [target, eventTypes, onEvent, options]
27 | )
28 | return null
29 | }
30 | )
31 |
--------------------------------------------------------------------------------
/src/dom.ts:
--------------------------------------------------------------------------------
1 | // Add event listeners to a list of events on a target; return a function to remove them
2 | export const addEventListeners = (
3 | target: undefined | HTMLElement,
4 | types: undefined | string[],
5 | listener: EventListenerOrEventListenerObject,
6 | options?: boolean | AddEventListenerOptions
7 | ) => {
8 | if (!target) return () => undefined
9 | types?.forEach((type) => target.addEventListener(type, listener, options))
10 | return () =>
11 | types?.forEach((type) =>
12 | target.removeEventListener(type, listener, options)
13 | )
14 | }
15 |
16 | // Convert a PointerEvent "buttons" prop into an array of booleans
17 | export const bitmaskToArray = (i: number) => [
18 | !!(i & 1),
19 | !!(i & 2),
20 | !!(i & 4),
21 | !!(i & 8),
22 | !!(i & 16)
23 | ]
24 |
25 | // PreventDefault on a list of events
26 | export const addPreventDefaults = (
27 | target: undefined | HTMLElement,
28 | types: undefined | string[]
29 | ) => addEventListeners(target, types, (event) => event.preventDefault())
30 |
--------------------------------------------------------------------------------
/src/camera.ts:
--------------------------------------------------------------------------------
1 | // TODO: Only import specific MathUtils
2 | import { Vector2, MathUtils } from 'three'
3 |
4 | // Reusable vectors (to save on GC)
5 | const handleMoveVector = new Vector2()
6 | const vector00 = new Vector2(0, 0)
7 |
8 | // Rotate X/Y vector by phi
9 | // TODO: Give this a better name, change arguments
10 | export const getScreenXY = ({ movementX, movementY, phi }) =>
11 | handleMoveVector
12 | .set(movementX, movementY)
13 | .rotateAround(vector00, -phi) // Rotate screen X/Y coords to match camera rotation
14 | .toArray()
15 |
16 | // Normalize phi and constrain r & theta for an orbit camera.
17 | // This is basically half of an OrbitCamera (the 3D spherical camera transform is the other half).
18 | // TODO: Give this a better name
19 | export const normalizeCoords = (
20 | { minR = 0, maxR = Infinity, minTheta = 0, maxTheta = Math.PI / 2 } = {},
21 | [r, theta, phi]: number[]
22 | ) => [
23 | MathUtils.clamp(r, minR, maxR),
24 | MathUtils.clamp(theta, minTheta, maxTheta),
25 | MathUtils.euclideanModulo(phi, Math.PI * 2)
26 | ]
27 |
--------------------------------------------------------------------------------
/examples/r3f-zustand/src/Controls/TEMPORARY_control-kit/dom.ts:
--------------------------------------------------------------------------------
1 | // Add event listeners to a list of events on a target; return a function to remove them
2 | export const addEventListeners = (
3 | target: undefined | HTMLElement,
4 | types: undefined | string[],
5 | listener: EventListenerOrEventListenerObject,
6 | options?: boolean | AddEventListenerOptions
7 | ) => {
8 | if (!target) return () => undefined
9 | types?.forEach((type) => target.addEventListener(type, listener, options))
10 | return () =>
11 | types?.forEach((type) =>
12 | target.removeEventListener(type, listener, options)
13 | )
14 | }
15 |
16 | // Convert a PointerEvent "buttons" prop into an array of booleans
17 | export const bitmaskToArray = (i: number) => [
18 | !!(i & 1),
19 | !!(i & 2),
20 | !!(i & 4),
21 | !!(i & 8),
22 | !!(i & 16)
23 | ]
24 |
25 | // PreventDefault on a list of events
26 | export const addPreventDefaults = (
27 | target: undefined | HTMLElement,
28 | types: undefined | string[]
29 | ) => addEventListeners(target, types, (event) => event.preventDefault())
30 |
--------------------------------------------------------------------------------
/examples/r3f-zustand/src/Controls/TEMPORARY_control-kit/camera.ts:
--------------------------------------------------------------------------------
1 | // TODO: Only import specific MathUtils
2 | import { Vector2, MathUtils } from 'three'
3 |
4 | // Reusable vectors (to save on GC)
5 | const handleMoveVector = new Vector2()
6 | const vector00 = new Vector2(0, 0)
7 |
8 | // Rotate X/Y vector by phi
9 | // TODO: Give this a better name, change arguments
10 | export const getScreenXY = ({ movementX, movementY, phi }) =>
11 | handleMoveVector
12 | .set(movementX, movementY)
13 | .rotateAround(vector00, -phi) // Rotate screen X/Y coords to match camera rotation
14 | .toArray()
15 |
16 | // Normalize phi and constrain r & theta for an orbit camera.
17 | // This is basically half of an OrbitCamera (the 3D spherical camera transform is the other half).
18 | // TODO: Give this a better name
19 | export const normalizeCoords = (
20 | { minR = 0, maxR = Infinity, minTheta = 0, maxTheta = Math.PI / 2 } = {},
21 | [r, theta, phi]: number[]
22 | ) => [
23 | MathUtils.clamp(r, minR, maxR),
24 | MathUtils.clamp(theta, minTheta, maxTheta),
25 | MathUtils.euclideanModulo(phi, Math.PI * 2)
26 | ]
27 |
--------------------------------------------------------------------------------
/examples/r3f-zustand/src/CameraHints.tsx:
--------------------------------------------------------------------------------
1 | import { useRef, useEffect } from 'react'
2 |
3 | // Updates to this component are slower,
4 | // as they must pass through Reacts reconciliation system.
5 | export const CameraHintDeclarative = ({ origin }) => {
6 | return (
7 |
8 |
9 |
10 |
11 |
12 |
13 | )
14 | }
15 |
16 | // Updates to this component are faster, as they bypass React reconciliation.
17 | // The downside is far less maintainable code.
18 | export const CameraHintImperative = ({ updateStream }) => {
19 | const ref = useRef()
20 | useEffect(
21 | () =>
22 | // Take a stream of {origin,coords} updates and imperatively move camera.
23 | // Escape hatch for bypassing the React update system (i.e. for animations).
24 | updateStream &&
25 | updateStream(({ origin }) => {
26 | if (!ref.current) return
27 | if (origin) ref.current.position.set(...origin)
28 | }),
29 | [updateStream]
30 | )
31 | return (
32 |
33 |
34 |
35 |
36 |
37 |
38 | )
39 | }
40 |
--------------------------------------------------------------------------------
/examples/r3f-zustand/src/Controls/cameraState.ts:
--------------------------------------------------------------------------------
1 | import { create } from 'zustand'
2 |
3 | export const useCameraConfig = create((set) => ({
4 | minR: 1,
5 | maxR: Infinity,
6 | minTheta: Math.PI / 16,
7 | maxTheta: Math.PI / 2,
8 | defaultOrigin: [0, 0, 0],
9 | defaultCoords: [1, Math.PI / 4, 0],
10 | updateCameraConfig: (updater: Function) =>
11 | set(
12 | (state) => updater(state) ?? {} // Ignore null/undefined
13 | )
14 | }))
15 |
16 | export const { updateCameraConfig } = useCameraConfig.getState()
17 |
18 | export const useCamera = create(() => ({
19 | origin: useCameraConfig.getState().defaultOrigin,
20 | coords: useCameraConfig.getState().defaultCoords
21 | }))
22 |
23 | // If you were to keep the updateCamera action inside your Zustand state:
24 | //
25 | // export const useCamera = create((set) => ({
26 | // origin: useCameraConfig.getState().defaultOrigin,
27 | // coords: useCameraConfig.getState().defaultCoords,
28 | // updateCamera: (updater: Function) =>
29 | // set(
30 | // (state) => updater(state) ?? {} // Ignore null/undefined
31 | // )
32 | // }))
33 | //
34 | // export const { updateCamera } = useCamera.getState()
35 |
36 | // Instead, we're doing a manual imperative update (for perf etc)
37 | export const updateCamera = (updater) => {
38 | const oldState = useCamera.getState()
39 | const state = updater(oldState)
40 | if (state === undefined || state === null) return
41 | if (!state.origin) state.origin = oldState.origin
42 | if (!state.coords) state.coords = oldState.coords
43 | useCamera.setState(state)
44 | }
45 |
--------------------------------------------------------------------------------
/examples/EXAMPLE-React-props-useState.md:
--------------------------------------------------------------------------------
1 | # React props/`useState` example
2 |
3 | Directly setting `origin`/`coords` props (such as with `useState`) is idiomatic, but not should not be used, as React reconciliation adds excessive latency.
4 |
5 | TODO: Benchmark `updateStream` vs `memo` + props with the following datatypes to see if this advice has changed:
6 |
7 | - `Array`
8 | - [Tuple](https://github.com/tc39/proposal-record-tuple)
9 | - [List](https://github.com/funkia/list)
10 |
11 | ```js
12 | import { useState } from 'react'
13 | import { Canvas } from '@react-three/fiber'
14 | import { SphericalCamera, normalizeCoords, bitmaskToArray } from 'control-kit'
15 |
16 | function App() {
17 | const [origin, setOrigin] = useState([0, 0, 0])
18 | const [coords, setCoords] = useState([16, Math.PI / 4, 0])
19 | const [cameraConfig, setCameraConfig] = useState({
20 | minR: 1,
21 | maxR: Infinity,
22 | minTheta: Math.PI / 16,
23 | maxTheta: Math.PI / 2
24 | })
25 |
26 | const mouseMoveHandler = ({ buttons, movementX, movementY }) => {
27 | const [leftButton, rightButton] = bitmaskToArray(buttons)
28 | if (leftButton) {
29 | // Rotate camera
30 | const [r, theta, phi] = coords
31 | const newCoords = normalizeCoords(cameraConfig, [
32 | r,
33 | theta + movementY / 100,
34 | phi - movementX / 100
35 | ])
36 | setCoords(newCoords)
37 | }
38 | }
39 |
40 | return (
41 |
44 | )
45 | }
46 | ```
47 |
--------------------------------------------------------------------------------
/examples/r3f-zustand/README.md:
--------------------------------------------------------------------------------
1 | # examples/r3f-zustand
2 |
3 | This is the "kitchen sink" example of an orbit camera controller in react-three-fiber, using Zustand for state management.
4 |
5 | **Note:** Changes to <./src/Controls> must follow semver!
6 |
7 | To try it, run:
8 |
9 | ```sh
10 | npm install
11 | npm run dev
12 | ```
13 |
14 | ## Usage as a module
15 |
16 | This example can be used as a batteries-included orbit camera controller:
17 |
18 | ```js
19 | import {
20 | Controls,
21 | useCamera,
22 | updateCamera,
23 | useCameraConfig,
24 | updateCameraConfig
25 | } from `control-kit/examples/r3f-zustand/src/Controls`
26 | ```
27 |
28 | ### Initial state
29 |
30 | For complex apps, we recommend copying the entire `examples/r3f-zustand/Controls` directory, and editing the initial camera config yourself.
31 | However, if you're just importing `examples/r3f-zustand/Controls` directly, this is a pattern for setting initial state:
32 |
33 | ```js
34 | import { useEffect } from 'react'
35 | import {
36 | Controls,
37 | updateCamera,
38 | useCameraConfig,
39 | updateCameraConfig
40 | } from './Controls'
41 |
42 | export const App = () => {
43 | useEffect(() =>
44 | // Set initial camera config; update camera
45 | {
46 | const cameraConfig = {
47 | defaultOrigin: [0, 0.5, 0],
48 | defaultCoords: [5, Math.PI / 4, Math.PI / 8]
49 | }
50 | updateCameraConfig(() => cameraConfig)
51 | updateCamera(() => ({
52 | origin: cameraConfig.defaultOrigin,
53 | coords: cameraConfig.defaultCoords
54 | }))
55 | }, []) // Only run on initial component mount
56 | return (
57 |
60 | )
61 | }
62 | ```
63 |
--------------------------------------------------------------------------------
/src/SphericalCamera.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef } from 'react'
2 | import { invalidate } from '@react-three/fiber'
3 | import { PerspectiveCamera } from '@react-three/drei'
4 |
5 | export const SphericalCamera = ({
6 | makeDefault = true, // Make this the default three camera
7 | updateStream, // Stream of { origin, coords } updates
8 | // TODO: Depreciate origin/coords in this type (hopefully we can recommend them in the future)
9 | origin = [0, 0, 0], // Target position
10 | coords = [0, 0, 0], // Camera rotation
11 | ...cameraProps
12 | }) => {
13 | const groupRef = useRef()
14 | const cameraRef = useRef()
15 | // TODO: Show a development-mode console depreciation warning if `origin`/`coords` is truthy
16 | useEffect(
17 | () =>
18 | // Subscribe to a stream of {origin,coords} updates and imperatively move camera.
19 | // Escape hatch for bypassing React diffing.
20 | updateStream &&
21 | updateStream(({ origin, coords }) => {
22 | if (!groupRef.current || !cameraRef.current) return
23 | if (origin) groupRef.current.position.set(...origin)
24 | if (coords) {
25 | const [r, theta, phi] = coords
26 | cameraRef.current.position.x = r
27 | groupRef.current.rotation.z = theta
28 | groupRef.current.rotation.y = phi
29 | }
30 | if (origin || coords) invalidate()
31 | }),
32 | [updateStream, invalidate] // TODO: Invalidate probably shouldn't be a dependency
33 | )
34 | const [r, theta, phi] = coords
35 | // TODO: Use maths, not a https://github.com/garbo-succus/control-kit/issues/6
36 | return (
37 |
38 |
45 |
46 | )
47 | }
48 |
--------------------------------------------------------------------------------
/examples/r3f-zustand/src/Controls/index.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import { useThree } from '@react-three/fiber'
3 | // TODO: Remove `TEMPORARY_control-kit` - https://github.com/garbo-succus/control-kit/issues/7
4 | import { ControlRig, SphericalCamera } from './TEMPORARY_control-kit'
5 | import { useCamera, updateCamera } from './cameraState'
6 | import { eventHandler } from './eventHandler'
7 |
8 | export const Controls = () => {
9 | // Get r3f canvas element
10 | const target = useThree((three) => three.gl.domElement)
11 | // Force an initial updateStream state update to fire
12 | useEffect(() => void updateCamera(() => ({})), [])
13 | return (
14 | <>
15 | {/*
16 | This would be idiomatic React:
17 | const origin = useCamera((s) => s.origin)
18 | const coords = useCamera((s) => s.coords)
19 | return ( ...
20 |
21 | ... )
22 | However, with updateStream, we can bypass diffing for better perf and make Paul happy.
23 | */}
24 |
25 |
26 | {/* ControlRig lets us attach event handlers from anywhere in the app */}
27 |
42 | >
43 | )
44 | }
45 |
46 | // Export `examples/r3f-zustand/Controls` as a library
47 | export {
48 | useCamera,
49 | updateCamera,
50 | useCameraConfig,
51 | updateCameraConfig
52 | } from './cameraState'
53 | export { eventHandler, handlePointer, handleWheel } from './eventHandler'
54 |
--------------------------------------------------------------------------------
/examples/r3f-zustand/src/Controls/TEMPORARY_control-kit/SphericalCamera.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef } from 'react'
2 | import { invalidate } from '@react-three/fiber'
3 | import { PerspectiveCamera } from '@react-three/drei'
4 |
5 | export const SphericalCamera = ({
6 | makeDefault = true, // Make this the default three camera
7 | updateStream, // Stream of { origin, coords } updates
8 | // TODO: Depreciate origin/coords in this type (hopefully we can recommend them in the future)
9 | origin = [0, 0, 0], // Target position
10 | coords = [0, 0, 0], // Camera rotation
11 | ...cameraProps
12 | }) => {
13 | const groupRef = useRef()
14 | const cameraRef = useRef()
15 | // TODO: Show a development-mode console depreciation warning if `origin`/`coords` is truthy
16 | useEffect(
17 | () =>
18 | // Subscribe to a stream of {origin,coords} updates and imperatively move camera.
19 | // Escape hatch for bypassing React diffing.
20 | updateStream &&
21 | updateStream(({ origin, coords }) => {
22 | if (!groupRef.current || !cameraRef.current) return
23 | if (origin) groupRef.current.position.set(...origin)
24 | if (coords) {
25 | const [r, theta, phi] = coords
26 | cameraRef.current.position.x = r
27 | groupRef.current.rotation.z = theta
28 | groupRef.current.rotation.y = phi
29 | }
30 | if (origin || coords) invalidate()
31 | }),
32 | [updateStream, invalidate] // TODO: Invalidate probably shouldn't be a dependency
33 | )
34 | const [r, theta, phi] = coords
35 | // TODO: Use maths, not a https://github.com/garbo-succus/control-kit/issues/6
36 | return (
37 |
38 |
45 |
46 | )
47 | }
48 |
--------------------------------------------------------------------------------
/examples/r3f-zustand/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import { Canvas } from '@react-three/fiber'
3 | import {
4 | Controls,
5 | useCamera,
6 | updateCamera,
7 | useCameraConfig,
8 | updateCameraConfig
9 | } from './Controls'
10 | import { CameraHintImperative, CameraHintDeclarative } from './CameraHints'
11 |
12 | export default function App() {
13 | const origin = useCamera((s) => s.origin)
14 |
15 | // NOTE: For complex apps, we recommend copying the entire `examples/r3f-zustand/Controls` directory,
16 | // and editing the initial camera config yourself.
17 | // However, if you're just importing `examples/r3f-zustand/Controls` directly,
18 | // this is a pattern for setting initial state:
19 | useEffect(() =>
20 | // Set initial camera config; update camera
21 | {
22 | const config = {
23 | defaultOrigin: [0, 0.5, 0],
24 | defaultCoords: [5, Math.PI / 4, Math.PI / 8]
25 | }
26 | updateCameraConfig(() => config)
27 | updateCamera(() => ({
28 | origin: config.defaultOrigin,
29 | coords: config.defaultCoords
30 | }))
31 | }, []) // Only run on initial component mount
32 | return (
33 |
56 | )
57 | }
58 |
--------------------------------------------------------------------------------
/examples/r3f-zustand/src/Controls/eventHandler.ts:
--------------------------------------------------------------------------------
1 | // TODO: Remove `TEMPORARY_control-kit` - https://github.com/garbo-succus/control-kit/issues/7
2 | import {
3 | normalizeCoords,
4 | bitmaskToArray,
5 | getScreenXY
6 | } from './TEMPORARY_control-kit'
7 | import { useCameraConfig, updateCamera } from './cameraState'
8 |
9 | export const eventHandler = (event) => {
10 | const cameraConfig = useCameraConfig.getState()
11 | switch (event.type) {
12 | case 'pointerup':
13 | case 'pointerdown':
14 | case 'pointermove':
15 | return updateCamera(handlePointer(event, cameraConfig))
16 | case 'wheel':
17 | return updateCamera(handleWheel(event, cameraConfig))
18 | default:
19 | return
20 | }
21 | }
22 |
23 | export const handlePointer =
24 | ({ buttons, movementX, movementY }: PointerEvent, cameraConfig: Object) =>
25 | ({ origin: [x, y, z], coords: [r, theta, phi] }) => {
26 | const [leftButton, rightButton, middleButton] = bitmaskToArray(buttons)
27 |
28 | // Simple (imperfect) way to scale mouse movement to camera movement
29 | // TODO: Match pointer movement to origin movement when looking straight down
30 | // Set the camera coords to [10, Math.PI / 2, 0] (i.e. looking straight down).
31 | // Pick a reference point at coords [0, 0, 0] and move the camera (origin [x, , z]).
32 | // The reference point should follow the cursor position exactly throughout the entire gesture.
33 | // Try the same at coords [100, Math.PI / 2, 0] etc.
34 | // NB: We want to ignore theta when gesturing along the screen Y axis,
35 | // so that pan gesture speed is always consistent regardless of camera rotation.
36 | const scaling = 100
37 |
38 | // Move camera (origin)
39 | if (leftButton) {
40 | const screenXY = getScreenXY({ movementX, movementY, phi })
41 | // TODO: Remove this when we fix scaling
42 | .map((v: number) => (v * r) / (scaling * 10)) // Move faster as we move out further
43 | const origin = [x - screenXY[1], y, z + screenXY[0]]
44 | return { origin }
45 | }
46 |
47 | // Rotate camera (coords)
48 | if (rightButton) {
49 | const coords = normalizeCoords(cameraConfig, [
50 | r,
51 | theta + movementY / scaling,
52 | phi - movementX / scaling
53 | ])
54 | return { coords }
55 | }
56 |
57 | // Reset camera
58 | if (middleButton) {
59 | return {
60 | origin: cameraConfig.defaultOrigin,
61 | coords: cameraConfig.defaultCoords
62 | }
63 | }
64 | }
65 |
66 | export const handleWheel = (event: PointerEvent, cameraConfig: Object) => (state) => {
67 | // TODO: Add support for Safari touchpad gesture events
68 | const {
69 | altKey,
70 | ctrlKey // `true` on trackpads when pinch-zooming (or ctrl key is pressed)
71 | } = event
72 |
73 | // Move camera (origin) when alt+wheeling
74 | if (altKey) {
75 | // Swap deltaX/deltaY if user is holding the ctrl key,
76 | // so that 1-directional mouse scrollwheels can be used to move both forward/back and left/right
77 | const [deltaX, deltaY] = ctrlKey
78 | ? [event.deltaY, event.deltaX]
79 | : [event.deltaX, event.deltaY]
80 | const {
81 | origin: [x, y, z]
82 | } = state
83 | const scaling = 50
84 | const origin = [x - deltaX / scaling, y, z - deltaY / scaling]
85 | return { origin }
86 | }
87 |
88 | // Rotate camera (coords)
89 | const {
90 | coords: [r, theta, phi]
91 | } = state
92 | // Swap deltaY/deltaZ if user is pinching (or ctrl+wheeling)
93 | const [deltaY, deltaZ] = ctrlKey
94 | ? [event.deltaZ, event.deltaY]
95 | : [event.deltaY, event.deltaZ]
96 | const coords = normalizeCoords(cameraConfig, [
97 | r + deltaY / (500 / r), // Dolly faster as we move out further
98 | theta - deltaZ / 100,
99 | phi + event.deltaX / 200
100 | ])
101 | return { coords }
102 | }
103 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🕹️ control-kit
2 |
3 | This is a toolkit for implementing react-three-fiber camera controls.
4 |
5 | ## Rationale
6 |
7 | Camera controls are generally the main way humans interact with 3D apps.
8 | They must handle multiple input methods (touch, keyboard, mouse, gamepads, XR, pen, etc) but should get out of the way when not needed.
9 |
10 | We have found "kitchen sink" camera controllers to be difficult to integrate into state management, and easy to outgrow.
11 |
12 | Rather than ship an entire controller, we aim to provide composable pieces for building one, patterns for common requirements, and boilerplate to get started with.
13 |
14 | ## Status: Alpha
15 |
16 | This library is brand new and currently awaiting feedback.
17 | Please DM me (neftaly) on the garbo.succus.games [Discord](https://discord.gg/sJVbJcd) with any comments, questions, or encouragement.
18 |
19 | - Incomplete types
20 | - No tests
21 | - No gestures <./examples/EXAMPLE-WIP-multitouch-gestures.md>
22 | - These functions will be renamed:
23 | - `normalizeCoords`
24 | - `getScreenXY`
25 | - These functions will have their signature (args, arg order) changed:
26 | - `getScreenXY`
27 | - `` is positioned with `` (some Drei components don't work correctly)
28 | - Major API changes not expected
29 |
30 | ## Example
31 |
32 | To see the full importable <./examples/r3f-zustand> example, run `npm start`.
33 |
34 | ```js
35 | import { Canvas, useThree } from '@react-three/fiber'
36 | import { create } from 'zustand'
37 | import {
38 | ControlRig,
39 | SphericalCamera,
40 | bitmaskToArray,
41 | normalizeCoords
42 | } from 'control-kit'
43 |
44 | const cameraConfig = {
45 | minR: 1,
46 | maxR: Infinity,
47 | minTheta: Math.PI / 16,
48 | maxTheta: Math.PI / 2
49 | }
50 |
51 | const useCamera = create(() => ({
52 | origin: [0, 0, 0],
53 | coords: [0, 0, 0]
54 | }))
55 |
56 | const eventHandler = ({ buttons, movementX, movementY }) => {
57 | const {
58 | origin,
59 | coords: [r, theta, phi]
60 | } = useCamera.getState()
61 | const [leftButton, rightButton] = bitmaskToArray(buttons)
62 | if (leftButton) {
63 | // Rotate camera
64 | const coords = normalizeCoords(cameraConfig, [
65 | r,
66 | theta + movementY / 100,
67 | phi - movementX / 100
68 | ])
69 | useCamera.setState({ origin, coords })
70 | }
71 | }
72 |
73 | const OrbitControls = () => {
74 | const target = useThree((three) => three.gl.domElement) // Canvas DOM element
75 | return (
76 | <>
77 |
78 |
83 | >
84 | )
85 | }
86 |
87 | export const App = () => (
88 |
91 | )
92 | ```
93 |
94 | ## API: DOM
95 |
96 | These functions are for input handling, independent of r3f.
97 |
98 | ### `ControlRig`
99 |
100 | This convenience component attaches an `onEvent` event handler to a `target` element, for every event listed in `eventTypes`.
101 | The `options` prop to is passed to the `addEventListener` call.
102 |
103 | `event.preventDefault()` is called on all events in the `preventDefaults` prop.
104 | See the note in for advice on which events to use.
105 |
106 | ```js
107 | const App = () => {
108 | const target = useThree((three) => three.gl.domElement) // Get r3f canvas DOM element
109 | return (
110 |
127 | )
128 | }
129 | ```
130 |
131 | ### `bitmaskToArray`
132 |
133 | This function converts `event.buttons` from a pointer event into an array of booleans.
134 |
135 | ```js
136 | const onPointerMove = (event) => {
137 | const [
138 | leftMouseButton,
139 | rightMouseButton,
140 | middleMouseButton,
141 | mouseButton4,
142 | mouseButton5
143 | ] = bitmaskToArray(event.buttons)
144 | }
145 | ```
146 |
147 | ### `addEventListeners`
148 |
149 | Adds event listeners for list of events to a target; returns a function to remove them.
150 | See .
151 |
152 | **Note:** You should avoid using mouse and touch events, as they have been obsoleted by pointer events.
153 |
154 | **Note:** You should avoid using `contextmenu` events.
155 | They are not supported on iOS.
156 | Unless you have a specific need for `contextmenu`, you should use a right-click or long-press gesture instead.
157 |
158 | ```js
159 | const removeEventListeners = addEventListeners(
160 | document.getElementById('mydiv'),
161 | [
162 | 'wheel',
163 | 'pointerover',
164 | 'pointerenter',
165 | 'pointerdown',
166 | 'pointermove',
167 | 'pointerup',
168 | 'pointercancel',
169 | 'pointerout'
170 | ],
171 | (event) => console.log(event.type),
172 | { capture: false, once: false, passive: false }
173 | )
174 | ```
175 |
176 | ### `addPreventDefaults`
177 |
178 | Adds `event.preventDefault()` to a list of events; returns a function to remove them.
179 |
180 | ```js
181 | const removePreventDefaults = addPreventDefaults(
182 | document.getElementById('mydiv'),
183 | ['wheel', 'touchstart', 'contextmenu']
184 | )
185 | ```
186 |
187 | **Note:** You should preventDefault these events:
188 |
189 | ```js
190 | const types = [
191 | // Block ctrl + mousewheel (zoom in/out on most browsers)
192 | // Block alt + mousewheel (history gesture in Firefox)
193 | 'wheel',
194 |
195 | // Block iOS swipe history gesture
196 | 'touchstart',
197 |
198 | // Stop right-clicks from opening the browser context menu
199 | 'contextmenu'
200 | ]
201 | ```
202 |
203 | ## API: Camera
204 |
205 | These functions are specifically for handling 3D cameras.
206 |
207 | 
208 |
209 | ```js
210 | // origin: target world position
211 | const [x, y, z] = origin
212 |
213 | // coords: camera rotation, relative to origin
214 | const [
215 | r, // Distance to origin
216 | theta, // Polar (up-down) angle
217 | phi // Azimuthal (left-right) angle
218 | ] = coords
219 | ```
220 |
221 | ### `SphericalCamera`
222 |
223 | This component creates a `` looking at `origin`, rotated by `coords`.
224 | It accepts all PerspectiveCamera props (`makeDefault` is true by default).
225 |
226 | For performance reasons you should use `updateStream` instead of the `origin` & `coords` props.
227 |
228 | The `updateStream` prop is provided as an escape hatch to bypass React reconciliation with imperative `{origin?,coords?}` updates.
229 | For further discussion see <./examples/EXAMPLE-React-props-useState.md>.
230 |
231 | ### `normalizeCoords`
232 |
233 | **Note:** This function will be renamed in the future.
234 |
235 | This function takes a `coords` array and:
236 |
237 | - Constrains `r` (min/max distance to/from origin)
238 | - Constrains `theta` (min/max up/down angle)
239 | - Normalizes `phi` to 0 <> 2π rads (if we rotated 360°, reset to 0°)
240 |
241 | ```js
242 | const [r, theta, phi] = normalizeCoords(
243 | {
244 | minR: 0,
245 | maxR: Infinity,
246 | minTheta: 0,
247 | maxTheta: Math.PI / 2
248 | },
249 | coords
250 | )
251 | ```
252 |
253 | ### `getScreenXY`
254 |
255 | **Note:** This function will be renamed in the future.
256 |
257 | **Note:** The arguments and return values for this function will be changed in the future.
258 |
259 | This function takes `movementX`, `movementY` from a PointerEvent and rotates them around `phi`.
260 |
261 | ## Notes
262 |
263 | ### Trackpad `wheel` events
264 |
265 | Most laptop trackpads support multitouch gestures.
266 |
267 | Firefox and Chrome handle them as `WheelEvent`s with `ctrlKey: true`.
268 |
269 | - 2-finger swipes behave like 2D mousewheel events (`deltaX`, `deltaY`).
270 | - 2-finger pinch (pinch-zooming) happens in the `deltaZ` direction.
271 | - 2-finger rotation gestures are not supported.
272 |
273 | [Diagram showing 2-finger swipe, pinch, and rotation multitouch gestures]
274 |
275 | Safari has more advanced support via the proprietary `gesturestart`/`gesturechange`/`gestureend` events; unfortunately the author has no experience with these.
276 |
277 | - A shim for Firefox/Chrome-style wheel event behavior in Safari should be found or made available.
278 | - An example handler for Safari gesture events should be made available.
279 |
280 | 2-finger-swipes can travel in both X and Y directions simultaneously, however the gesture may have to be initiated with diagonal touchpad movement (depending on firmware), otherwise it may only move along one axis. Users should be made aware of this in onboarding.
281 |
282 | It is recommended to show the "ctrl" key as a wheel modifier key in keyboard config interfaces, but to prevent it from being changed.
283 |
284 | ### `touchAction: 'none'`
285 |
286 | > Make sure you always set [touchAction](https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action) on a draggable element to prevent glitches with the browser native scrolling on touch devices
287 |
288 | -- [pmndrs/use-gesture](https://github.com/pmndrs/use-gesture)
289 |
290 | ### In-app cursors
291 |
292 | There is latency between the actual mouse cursor position and the position reported to the DOM.
293 | This can be visualized here:
294 |
295 | It is possible to mask this latency by hiding the system cursor and showing your own cursor (an emoji or SVG etc).
296 |
297 | This is generally not recommended, as it makes a11y worse, the cursor may fail to load, and there is discontinuity for the user when entering/leaving the window. However, in certain contexts (fullscreen apps etc), showing an in-app cursor can help with pointing precision and game feedback.
298 |
--------------------------------------------------------------------------------
/camera-state.svg:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/examples/EXAMPLE-WIP-multitouch-gestures.md:
--------------------------------------------------------------------------------
1 | # (Multitouch) gestures - WIP
2 |
3 | This gesture handler works by processing pointer events in a [Flyd](https://github.com/paldepind/flyd) reducer.
4 |
5 | ## Planned
6 |
7 | ~~I hope to implement these before September 2023:~~
8 | [Update Sep 2023]: My priorities have changed however this will still be done! I am working on integrating this with
9 |
10 | - short-press (tap)
11 | - drag
12 | - 2-finger pinch + rotation (ignore everything except the first 2 fingers)
13 |
14 | ## Unplanned
15 |
16 | These are out-of-scope (barring contributions, sponsorship, or trivial implementation):
17 |
18 | - long-press
19 | - 2-finger swipe
20 | - throw (finish a drag or 2+-finger swipe over a velocity threshold)
21 | - double-short-press (doubletap)
22 | - n-short-press (improved doubletap with any number of taps)
23 | - n-finger pinch + rotation, swipe (currently we ignore everything except the first 2 fingers)
24 | - n-finger drag (multiple fingers dragging different things simultaneously)
25 | - n-t-press (improved doubletap with any number of taps, of any time length - like morse code)
26 | - simultaneous independent pen input
27 |
28 | These would be cool to have, but aren't planned, and would require feasibility research:
29 |
30 | - glyphs (drag or use a pen to paint a shape; fuzzy match shape when finished)
31 | - n-hand 2-finger pinch + rotation, swipe (treat each pinch-zoom/swipe within an area approx the radius of a human hand as an individual gesture)
32 | - n-hand n-finger gestures (multiple independent multitouch gestures, as long as hands aren't too close together)
33 |
34 | ## 🚧 WORK IN PROGRESS 🚧
35 |
36 | This reducer could take a stream of DOM PointerEvents.
37 | It would then output a stream of GestureStream events (and optionally forward incoming events).
38 | It may be better to output React-style events and follow their design descisions, as long as this doesn't make interop worse.
39 |
40 | GestureStream events could have an API similar to PointerEvents, i.e. we treat different gestures as proprietary PointerEvents input devices.
41 |
42 | We could take a similar approach for turning WheelEvents and Safari touchpad gesture events into GestureStream TouchpadEvents or something.
43 |
44 | We should check the Gamepad, Keyboard, Web MIDI, and XR events and see if they could also be treated like proprietary PointerEvents (or visa-versa).
45 |
46 |
47 |
48 | _This is some old code from a prior multitouch implementation._
49 | _I want to break it up, refactor, and if possible reduce or eliminate the dependancy on Flyd._
50 |
51 | ### `types.d.ts`
52 |
53 | ```js
54 |
55 | type Vec2 = [number, number]
56 | type Vec3 = [number, number, number]
57 |
58 | interface GesturestreamEventGesture {
59 | touches: PointerEvent[]
60 | center: Vec2
61 | buttons: boolean[]
62 | deltaXY: Vec2
63 | targetSize?: Vec2
64 | angle?: number
65 | distance?: number
66 | deltaAngle?: number
67 | deltaDistance?: number
68 | wasTap?: boolean
69 | }
70 |
71 | interface GesturestreamEvent {
72 | event: PointerEvent
73 | gesture: GesturestreamEventGesture
74 | }
75 | ```
76 |
77 | ### `handlers.js`
78 |
79 | ```js
80 | export const handleRotate = ({
81 | deltaXY,
82 | targetSize
83 | }: GesturestreamEventGesture) =>
84 | actions.updatePosition(({ coords: [r, oldTheta, oldPhi] }: CameraState) => {
85 | const theta = oldTheta + (deltaXY[1] / targetSize[1]) * (Math.PI * 1)
86 | const phi = oldPhi - (deltaXY[0] / targetSize[0]) * (Math.PI * 4)
87 | return { coords: [r, theta, phi] }
88 | })
89 |
90 | export const handlePinch = ({
91 | deltaXY,
92 | deltaAngle,
93 | deltaDistance
94 | }: GesturestreamEventGesture) =>
95 | actions.updatePosition((state: CameraState) => {
96 | const {
97 | coords: [oldR, theta, oldPhi]
98 | } = state
99 | const { origin } = move(deltaXY)(state)
100 | const r = oldR + deltaDistance / (500 / oldR)
101 | const phi = oldPhi + deltaAngle
102 | return { origin, coords: [r, theta, phi] }
103 | })
104 | ```
105 |
106 | ### `gesturestream.js`
107 |
108 | ```js
109 | import flyd from 'flyd'
110 | import flydfilter from 'flyd/module/filter'
111 | import {
112 | assoc,
113 | dissoc,
114 | compose,
115 | values,
116 | filter,
117 | sortBy,
118 | take,
119 | map
120 | } from 'ramda'
121 | import flydDomEvents from 'flyd-dom-events'
122 |
123 | const eventTypes = [
124 | 'wheel',
125 | 'pointerover',
126 | 'pointerenter',
127 | 'pointerdown',
128 | 'pointermove',
129 | 'pointerup',
130 | 'pointercancel',
131 | 'pointerout'
132 | ]
133 |
134 | // Subtract a list of 2D vectors
135 | const subtract = (...args) =>
136 | args.reduce(([a1, a2], [b1, b2]) => [a1 - b1, a2 - b2])
137 |
138 | // Divide a list of 2D vectors
139 | const divide = ([a, b], d) => [a / d, b / d]
140 |
141 | // Add gesture information to an event
142 | const updateGesture = ({ gesture: lastGesture = {}, event: lastEvent }, e) => {
143 | const { touches: lastTouches, center: lastCenter } = lastGesture
144 | const { pointers, event } = e
145 | if (!event) return {} // flyd.scan emits empty value on init
146 |
147 | const touchList = compose(
148 | map(({ clientX, clientY }) => [clientX, clientY]),
149 | take(2), // Ignore 3+ finger gestures
150 | sortBy((event) => event.pointerId),
151 | filter((event) => event.pointerType === 'touch'),
152 | values
153 | )(pointers)
154 | const touches = touchList.length
155 |
156 | const getDeltaCenter = (center) =>
157 | lastCenter &&
158 | // Don't calculate delta if we've gone from multitouch to singletouch
159 | touches === lastTouches &&
160 | (lastEvent?.pointerType !== 'touch' || lastEvent?.type !== 'pointerout')
161 | ? subtract(center, lastCenter)
162 | : [0, 0]
163 |
164 | const gesture = (() => {
165 | const targetSize = [event.target.clientWidth, event.target.clientHeight]
166 | if (touches === 2) {
167 | const diff2 = subtract(...touchList)
168 | const center = subtract(touchList[0], divide(diff2, 2))
169 | const angle = Math.PI + Math.atan2(...diff2)
170 | const distance = Math.hypot(...diff2)
171 | return {
172 | targetSize,
173 | touches,
174 | center,
175 | buttons: bitmaskToArray(touchList[0].buttons),
176 | deltaXY: getDeltaCenter(center),
177 | angle,
178 | deltaAngle: (lastGesture.angle || angle) - angle,
179 | distance,
180 | deltaDistance: (lastGesture.distance || distance) - distance
181 | }
182 | } else {
183 | const center = [event.clientX, event.clientY]
184 | const wasTap =
185 | event.type === 'pointerup' && lastEvent?.type === 'pointerdown'
186 | const buttons =
187 | event.type === 'pointerup' && lastEvent
188 | ? lastEvent.buttons
189 | : event.buttons
190 | // Initial X/Y position of gesture (when gesturing)
191 | // TODO: Should this be implemented for multitouch?
192 | const start = event.type === 'pointerdown' ? center : lastGesture.start
193 | // Total X/Y change of gesture (when gesturing)
194 | // TODO: Should this be implemented for multitouch?
195 | const offset = start
196 | ? [center[0] - start[0], center[1] - start[1]]
197 | : [0, 0]
198 | return {
199 | targetSize,
200 | touches,
201 | center,
202 | buttons: bitmaskToArray(buttons),
203 | deltaXY: getDeltaCenter(center),
204 | start,
205 | offset,
206 | wasTap
207 | }
208 | }
209 | })()
210 | return { ...e, gesture }
211 | }
212 |
213 | // Update the 'pointers' prop with currently active pointers
214 | const updatePointers = ({ pointers: lastPointers }, e) => {
215 | const { event } = e
216 | const { pointerId, type } = event
217 | const pointers =
218 | pointerId === undefined || type === 'pointerout' || type === 'pointercancel'
219 | ? dissoc(pointerId, lastPointers)
220 | : assoc(pointerId, event, lastPointers)
221 | return { ...e, pointers }
222 | }
223 |
224 | // Returns a flyd stream of gesture events from a DOM object
225 | const gesturestream = (target, options) =>
226 | compose(
227 | flydfilter((e) => e.event), // flyd.scan emits empty value on init
228 | flyd.scan(updateGesture, {}),
229 | flyd.scan(updatePointers, {}),
230 | flyd.map((event) => ({ event })),
231 | flydDomEvents
232 | )(eventTypes, target, options)
233 |
234 | export default gesturestream
235 | ```
236 |
237 | ### `useControls.jsx`
238 |
239 | This is application-specific code
240 |
241 | ```js
242 | import { handlers } from 'r3f-orbit-controls'
243 | import { useObjectStore } from 'utils/Interactable'
244 | import { map, compose, sort, assocPath } from 'ramda'
245 | import { create } from 'zustand'
246 | import { Raycaster, Vector2 } from 'three'
247 | import useStore from 'model'
248 |
249 | const coords = new Vector2()
250 | const raycaster = new Raycaster()
251 | const updateRaycaster = (center, targetSize, camera) => {
252 | const x = (center[0] / targetSize[0]) * 2 - 1
253 | const y = -(center[1] / targetSize[1]) * 2 + 1
254 | coords.set(x, y)
255 | raycaster.setFromCamera(coords, camera)
256 | return raycaster
257 | }
258 |
259 | // Get a list of object-raycaster intersections
260 | const getIntersects = (raycaster, firstHitOnly) => {
261 | const { objects } = useObjectStore.getState()
262 | // Bubble up to find the parent Interactable group
263 | const getInteractables = (object) => {
264 | if (!object) return []
265 | const { handlers } = objects[object.uuid] || {}
266 | if (handlers) return [handlers, ...getInteractables(object.parent)]
267 | return getInteractables(object.parent)
268 | }
269 | const intersects = (() => {
270 | raycaster.firstHitOnly = !!firstHitOnly
271 | const i = raycaster.intersectObjects(
272 | Object.values(objects).map((o) => o.object),
273 | true
274 | )
275 | raycaster.firstHitOnly = false
276 | return i
277 | })()
278 | return compose(
279 | map((intersection) => ({
280 | intersection,
281 | handlers: getInteractables(intersection.object)
282 | })),
283 | sort((a, b) => {
284 | // Sort by depthTest = false (materials rendered on top)
285 | const dtA = a.object.material?.depthTest
286 | const dtB = b.object.material?.depthTest
287 | if (!dtA || !dtB) {
288 | if (dtA) return 1
289 | if (dtB) return -1
290 | }
291 | // Sort by renderOrder (like z-index, but only works when depthTest=false)
292 | const roA = (dtA && a.object.renderOrder) || 0
293 | const roB = (dtB && b.object.renderOrder) || 0
294 | if (roA || roB) return roA - roB
295 | // Sort by distance from camera
296 | return a.distance - b.distance
297 | })
298 | )(intersects)
299 | }
300 |
301 | // Trigger onGesture event on targetObject with args
302 | // TODO: Only have 1 handler per object
303 | const onGesture = (targetObject, ...args) =>
304 | targetObject?.handlers[0]?.onGesture?.(...args)
305 |
306 | const useControls = create((set, get) => ({
307 | isHovering: false,
308 | targetObject: undefined,
309 | isGesturing: false,
310 |
311 | handleGesture: (onTapMissed, threecamera) => (e) => {
312 | // Decide what to do with an onGesture event.
313 | // Generally, we're either updating the camera,
314 | // or fowarding pointer events to the handlers on a Piece.
315 | const { event, gesture } = e
316 | const { type, pointerType } = event
317 | const {
318 | center,
319 | targetSize,
320 | touches,
321 | buttons: [leftButton, rightButton, middleButton]
322 | } = gesture
323 | const raycaster = updateRaycaster(center, targetSize, threecamera)
324 | const isGesturing = touches > 0 || event.buttons !== 0
325 | const isHovering = getIntersects(raycaster, true).length > 0
326 | const { dragToPanCamera } = useStore.getState().settings
327 |
328 | const sendFakePointercancelEvent = (e, raycaster) =>
329 | set(({ targetObject }) => {
330 | onGesture(
331 | targetObject,
332 | assocPath(['event', 'type'], 'pointercancel', e),
333 | raycaster
334 | )
335 | return { targetObject: undefined, isGesturing, isHovering }
336 | })
337 |
338 | if (type === 'wheel') {
339 | handlers.handleWheel(event)
340 | }
341 |
342 | if (
343 | type === 'pointerdown' &&
344 | touches === 2 &&
345 | pointerType === 'touch' &&
346 | get().targetObject
347 | ) {
348 | // Drag started, but gesture became a multitouch
349 | return sendFakePointercancelEvent(e, raycaster)
350 | }
351 |
352 | if (type === 'pointermove') {
353 | if (pointerType === 'touch' && touches === 2) {
354 | handlers.handlePinch(gesture)
355 | } else if (rightButton) {
356 | handlers.handleRotate(gesture)
357 | } else if (leftButton || middleButton || pointerType === 'touch') {
358 | const { targetObject } = get()
359 | if (
360 | middleButton ||
361 | (!targetObject && (dragToPanCamera || !leftButton))
362 | ) {
363 | handlers.handleMove(gesture)
364 | } else {
365 | onGesture(targetObject, e, raycaster)
366 | }
367 | }
368 | } else if (touches < 2) {
369 | if (type === 'pointerdown') {
370 | const intersects = getIntersects(raycaster)
371 | if (intersects.length > 0) {
372 | const targetObject = intersects[0]
373 | onGesture(targetObject, e, raycaster)
374 | return set((state) => ({ targetObject, isGesturing, isHovering }))
375 | }
376 | } else if (type === 'pointerup' || type === 'pointercancel') {
377 | return set(({ targetObject }) => {
378 | if (gesture.wasTap && !targetObject) {
379 | onTapMissed(e, raycaster)
380 | } else {
381 | onGesture(targetObject, e, raycaster)
382 | }
383 | return { targetObject: undefined, isGesturing, isHovering }
384 | })
385 | } else if (
386 | type === 'pointerenter' &&
387 | !leftButton &&
388 | pointerType !== 'touch' &&
389 | get().targetObject
390 | ) {
391 | // User dragged out-of-bounds then released cursor
392 | return sendFakePointercancelEvent(e, raycaster)
393 | }
394 | }
395 | return set((state) => ({ isGesturing, isHovering }))
396 | }
397 | }))
398 |
399 | export default useControls
400 | ```
401 |
--------------------------------------------------------------------------------
/examples/r3f-zustand/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-r3f",
3 | "version": "0.0.0",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "example-r3f",
9 | "version": "0.0.0",
10 | "dependencies": {
11 | "@react-three/drei": "^9.66.1",
12 | "@react-three/fiber": "^8.13.0",
13 | "control-kit": "file:../",
14 | "react": "^18.2.0",
15 | "react-dom": "^18.2.0",
16 | "three": "^0.152.2",
17 | "zustand": "^4.3.7"
18 | },
19 | "devDependencies": {
20 | "@types/react": "^18.0.28",
21 | "@types/react-dom": "^18.0.11",
22 | "@vitejs/plugin-react-swc": "^3.0.0",
23 | "typescript": "^4.9.3",
24 | "vite": "^4.2.0"
25 | }
26 | },
27 | "..": {
28 | "version": "0.0.0",
29 | "dependencies": {
30 | "@react-three/drei": ">=9.53",
31 | "three": ">=0.149"
32 | },
33 | "devDependencies": {
34 | "@types/node": "^18.11.19",
35 | "@types/react": "^18.2.0",
36 | "@types/react-dom": "^18.2.0",
37 | "@types/three": "^0.149.0",
38 | "prettier-config-standard": "^5.0.0",
39 | "typescript": "^5.0.0"
40 | },
41 | "peerDependencies": {
42 | "react": ">=18.0"
43 | }
44 | },
45 | "node_modules/@babel/runtime": {
46 | "version": "7.21.5",
47 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz",
48 | "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==",
49 | "dependencies": {
50 | "regenerator-runtime": "^0.13.11"
51 | },
52 | "engines": {
53 | "node": ">=6.9.0"
54 | }
55 | },
56 | "node_modules/@chevrotain/cst-dts-gen": {
57 | "version": "10.5.0",
58 | "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.5.0.tgz",
59 | "integrity": "sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==",
60 | "dependencies": {
61 | "@chevrotain/gast": "10.5.0",
62 | "@chevrotain/types": "10.5.0",
63 | "lodash": "4.17.21"
64 | }
65 | },
66 | "node_modules/@chevrotain/gast": {
67 | "version": "10.5.0",
68 | "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-10.5.0.tgz",
69 | "integrity": "sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==",
70 | "dependencies": {
71 | "@chevrotain/types": "10.5.0",
72 | "lodash": "4.17.21"
73 | }
74 | },
75 | "node_modules/@chevrotain/types": {
76 | "version": "10.5.0",
77 | "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-10.5.0.tgz",
78 | "integrity": "sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A=="
79 | },
80 | "node_modules/@chevrotain/utils": {
81 | "version": "10.5.0",
82 | "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-10.5.0.tgz",
83 | "integrity": "sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ=="
84 | },
85 | "node_modules/@esbuild/android-arm": {
86 | "version": "0.17.18",
87 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.18.tgz",
88 | "integrity": "sha512-EmwL+vUBZJ7mhFCs5lA4ZimpUH3WMAoqvOIYhVQwdIgSpHC8ImHdsRyhHAVxpDYUSm0lWvd63z0XH1IlImS2Qw==",
89 | "cpu": [
90 | "arm"
91 | ],
92 | "dev": true,
93 | "optional": true,
94 | "os": [
95 | "android"
96 | ],
97 | "engines": {
98 | "node": ">=12"
99 | }
100 | },
101 | "node_modules/@esbuild/android-arm64": {
102 | "version": "0.17.18",
103 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.18.tgz",
104 | "integrity": "sha512-/iq0aK0eeHgSC3z55ucMAHO05OIqmQehiGay8eP5l/5l+iEr4EIbh4/MI8xD9qRFjqzgkc0JkX0LculNC9mXBw==",
105 | "cpu": [
106 | "arm64"
107 | ],
108 | "dev": true,
109 | "optional": true,
110 | "os": [
111 | "android"
112 | ],
113 | "engines": {
114 | "node": ">=12"
115 | }
116 | },
117 | "node_modules/@esbuild/android-x64": {
118 | "version": "0.17.18",
119 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.18.tgz",
120 | "integrity": "sha512-x+0efYNBF3NPW2Xc5bFOSFW7tTXdAcpfEg2nXmxegm4mJuVeS+i109m/7HMiOQ6M12aVGGFlqJX3RhNdYM2lWg==",
121 | "cpu": [
122 | "x64"
123 | ],
124 | "dev": true,
125 | "optional": true,
126 | "os": [
127 | "android"
128 | ],
129 | "engines": {
130 | "node": ">=12"
131 | }
132 | },
133 | "node_modules/@esbuild/darwin-arm64": {
134 | "version": "0.17.18",
135 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.18.tgz",
136 | "integrity": "sha512-6tY+djEAdF48M1ONWnQb1C+6LiXrKjmqjzPNPWXhu/GzOHTHX2nh8Mo2ZAmBFg0kIodHhciEgUBtcYCAIjGbjQ==",
137 | "cpu": [
138 | "arm64"
139 | ],
140 | "dev": true,
141 | "optional": true,
142 | "os": [
143 | "darwin"
144 | ],
145 | "engines": {
146 | "node": ">=12"
147 | }
148 | },
149 | "node_modules/@esbuild/darwin-x64": {
150 | "version": "0.17.18",
151 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.18.tgz",
152 | "integrity": "sha512-Qq84ykvLvya3dO49wVC9FFCNUfSrQJLbxhoQk/TE1r6MjHo3sFF2tlJCwMjhkBVq3/ahUisj7+EpRSz0/+8+9A==",
153 | "cpu": [
154 | "x64"
155 | ],
156 | "dev": true,
157 | "optional": true,
158 | "os": [
159 | "darwin"
160 | ],
161 | "engines": {
162 | "node": ">=12"
163 | }
164 | },
165 | "node_modules/@esbuild/freebsd-arm64": {
166 | "version": "0.17.18",
167 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.18.tgz",
168 | "integrity": "sha512-fw/ZfxfAzuHfaQeMDhbzxp9mc+mHn1Y94VDHFHjGvt2Uxl10mT4CDavHm+/L9KG441t1QdABqkVYwakMUeyLRA==",
169 | "cpu": [
170 | "arm64"
171 | ],
172 | "dev": true,
173 | "optional": true,
174 | "os": [
175 | "freebsd"
176 | ],
177 | "engines": {
178 | "node": ">=12"
179 | }
180 | },
181 | "node_modules/@esbuild/freebsd-x64": {
182 | "version": "0.17.18",
183 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.18.tgz",
184 | "integrity": "sha512-FQFbRtTaEi8ZBi/A6kxOC0V0E9B/97vPdYjY9NdawyLd4Qk5VD5g2pbWN2VR1c0xhzcJm74HWpObPszWC+qTew==",
185 | "cpu": [
186 | "x64"
187 | ],
188 | "dev": true,
189 | "optional": true,
190 | "os": [
191 | "freebsd"
192 | ],
193 | "engines": {
194 | "node": ">=12"
195 | }
196 | },
197 | "node_modules/@esbuild/linux-arm": {
198 | "version": "0.17.18",
199 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.18.tgz",
200 | "integrity": "sha512-jW+UCM40LzHcouIaqv3e/oRs0JM76JfhHjCavPxMUti7VAPh8CaGSlS7cmyrdpzSk7A+8f0hiedHqr/LMnfijg==",
201 | "cpu": [
202 | "arm"
203 | ],
204 | "dev": true,
205 | "optional": true,
206 | "os": [
207 | "linux"
208 | ],
209 | "engines": {
210 | "node": ">=12"
211 | }
212 | },
213 | "node_modules/@esbuild/linux-arm64": {
214 | "version": "0.17.18",
215 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.18.tgz",
216 | "integrity": "sha512-R7pZvQZFOY2sxUG8P6A21eq6q+eBv7JPQYIybHVf1XkQYC+lT7nDBdC7wWKTrbvMXKRaGudp/dzZCwL/863mZQ==",
217 | "cpu": [
218 | "arm64"
219 | ],
220 | "dev": true,
221 | "optional": true,
222 | "os": [
223 | "linux"
224 | ],
225 | "engines": {
226 | "node": ">=12"
227 | }
228 | },
229 | "node_modules/@esbuild/linux-ia32": {
230 | "version": "0.17.18",
231 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.18.tgz",
232 | "integrity": "sha512-ygIMc3I7wxgXIxk6j3V00VlABIjq260i967Cp9BNAk5pOOpIXmd1RFQJQX9Io7KRsthDrQYrtcx7QCof4o3ZoQ==",
233 | "cpu": [
234 | "ia32"
235 | ],
236 | "dev": true,
237 | "optional": true,
238 | "os": [
239 | "linux"
240 | ],
241 | "engines": {
242 | "node": ">=12"
243 | }
244 | },
245 | "node_modules/@esbuild/linux-loong64": {
246 | "version": "0.17.18",
247 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.18.tgz",
248 | "integrity": "sha512-bvPG+MyFs5ZlwYclCG1D744oHk1Pv7j8psF5TfYx7otCVmcJsEXgFEhQkbhNW8otDHL1a2KDINW20cfCgnzgMQ==",
249 | "cpu": [
250 | "loong64"
251 | ],
252 | "dev": true,
253 | "optional": true,
254 | "os": [
255 | "linux"
256 | ],
257 | "engines": {
258 | "node": ">=12"
259 | }
260 | },
261 | "node_modules/@esbuild/linux-mips64el": {
262 | "version": "0.17.18",
263 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.18.tgz",
264 | "integrity": "sha512-oVqckATOAGuiUOa6wr8TXaVPSa+6IwVJrGidmNZS1cZVx0HqkTMkqFGD2HIx9H1RvOwFeWYdaYbdY6B89KUMxA==",
265 | "cpu": [
266 | "mips64el"
267 | ],
268 | "dev": true,
269 | "optional": true,
270 | "os": [
271 | "linux"
272 | ],
273 | "engines": {
274 | "node": ">=12"
275 | }
276 | },
277 | "node_modules/@esbuild/linux-ppc64": {
278 | "version": "0.17.18",
279 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.18.tgz",
280 | "integrity": "sha512-3dLlQO+b/LnQNxgH4l9rqa2/IwRJVN9u/bK63FhOPB4xqiRqlQAU0qDU3JJuf0BmaH0yytTBdoSBHrb2jqc5qQ==",
281 | "cpu": [
282 | "ppc64"
283 | ],
284 | "dev": true,
285 | "optional": true,
286 | "os": [
287 | "linux"
288 | ],
289 | "engines": {
290 | "node": ">=12"
291 | }
292 | },
293 | "node_modules/@esbuild/linux-riscv64": {
294 | "version": "0.17.18",
295 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.18.tgz",
296 | "integrity": "sha512-/x7leOyDPjZV3TcsdfrSI107zItVnsX1q2nho7hbbQoKnmoeUWjs+08rKKt4AUXju7+3aRZSsKrJtaRmsdL1xA==",
297 | "cpu": [
298 | "riscv64"
299 | ],
300 | "dev": true,
301 | "optional": true,
302 | "os": [
303 | "linux"
304 | ],
305 | "engines": {
306 | "node": ">=12"
307 | }
308 | },
309 | "node_modules/@esbuild/linux-s390x": {
310 | "version": "0.17.18",
311 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.18.tgz",
312 | "integrity": "sha512-cX0I8Q9xQkL/6F5zWdYmVf5JSQt+ZfZD2bJudZrWD+4mnUvoZ3TDDXtDX2mUaq6upMFv9FlfIh4Gfun0tbGzuw==",
313 | "cpu": [
314 | "s390x"
315 | ],
316 | "dev": true,
317 | "optional": true,
318 | "os": [
319 | "linux"
320 | ],
321 | "engines": {
322 | "node": ">=12"
323 | }
324 | },
325 | "node_modules/@esbuild/linux-x64": {
326 | "version": "0.17.18",
327 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.18.tgz",
328 | "integrity": "sha512-66RmRsPlYy4jFl0vG80GcNRdirx4nVWAzJmXkevgphP1qf4dsLQCpSKGM3DUQCojwU1hnepI63gNZdrr02wHUA==",
329 | "cpu": [
330 | "x64"
331 | ],
332 | "dev": true,
333 | "optional": true,
334 | "os": [
335 | "linux"
336 | ],
337 | "engines": {
338 | "node": ">=12"
339 | }
340 | },
341 | "node_modules/@esbuild/netbsd-x64": {
342 | "version": "0.17.18",
343 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.18.tgz",
344 | "integrity": "sha512-95IRY7mI2yrkLlTLb1gpDxdC5WLC5mZDi+kA9dmM5XAGxCME0F8i4bYH4jZreaJ6lIZ0B8hTrweqG1fUyW7jbg==",
345 | "cpu": [
346 | "x64"
347 | ],
348 | "dev": true,
349 | "optional": true,
350 | "os": [
351 | "netbsd"
352 | ],
353 | "engines": {
354 | "node": ">=12"
355 | }
356 | },
357 | "node_modules/@esbuild/openbsd-x64": {
358 | "version": "0.17.18",
359 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.18.tgz",
360 | "integrity": "sha512-WevVOgcng+8hSZ4Q3BKL3n1xTv5H6Nb53cBrtzzEjDbbnOmucEVcZeGCsCOi9bAOcDYEeBZbD2SJNBxlfP3qiA==",
361 | "cpu": [
362 | "x64"
363 | ],
364 | "dev": true,
365 | "optional": true,
366 | "os": [
367 | "openbsd"
368 | ],
369 | "engines": {
370 | "node": ">=12"
371 | }
372 | },
373 | "node_modules/@esbuild/sunos-x64": {
374 | "version": "0.17.18",
375 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.18.tgz",
376 | "integrity": "sha512-Rzf4QfQagnwhQXVBS3BYUlxmEbcV7MY+BH5vfDZekU5eYpcffHSyjU8T0xucKVuOcdCsMo+Ur5wmgQJH2GfNrg==",
377 | "cpu": [
378 | "x64"
379 | ],
380 | "dev": true,
381 | "optional": true,
382 | "os": [
383 | "sunos"
384 | ],
385 | "engines": {
386 | "node": ">=12"
387 | }
388 | },
389 | "node_modules/@esbuild/win32-arm64": {
390 | "version": "0.17.18",
391 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.18.tgz",
392 | "integrity": "sha512-Kb3Ko/KKaWhjeAm2YoT/cNZaHaD1Yk/pa3FTsmqo9uFh1D1Rfco7BBLIPdDOozrObj2sahslFuAQGvWbgWldAg==",
393 | "cpu": [
394 | "arm64"
395 | ],
396 | "dev": true,
397 | "optional": true,
398 | "os": [
399 | "win32"
400 | ],
401 | "engines": {
402 | "node": ">=12"
403 | }
404 | },
405 | "node_modules/@esbuild/win32-ia32": {
406 | "version": "0.17.18",
407 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.18.tgz",
408 | "integrity": "sha512-0/xUMIdkVHwkvxfbd5+lfG7mHOf2FRrxNbPiKWg9C4fFrB8H0guClmaM3BFiRUYrznVoyxTIyC/Ou2B7QQSwmw==",
409 | "cpu": [
410 | "ia32"
411 | ],
412 | "dev": true,
413 | "optional": true,
414 | "os": [
415 | "win32"
416 | ],
417 | "engines": {
418 | "node": ">=12"
419 | }
420 | },
421 | "node_modules/@esbuild/win32-x64": {
422 | "version": "0.17.18",
423 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.18.tgz",
424 | "integrity": "sha512-qU25Ma1I3NqTSHJUOKi9sAH1/Mzuvlke0ioMJRthLXKm7JiSKVwFghlGbDLOO2sARECGhja4xYfRAZNPAkooYg==",
425 | "cpu": [
426 | "x64"
427 | ],
428 | "dev": true,
429 | "optional": true,
430 | "os": [
431 | "win32"
432 | ],
433 | "engines": {
434 | "node": ">=12"
435 | }
436 | },
437 | "node_modules/@react-spring/animated": {
438 | "version": "9.6.1",
439 | "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.6.1.tgz",
440 | "integrity": "sha512-ls/rJBrAqiAYozjLo5EPPLLOb1LM0lNVQcXODTC1SMtS6DbuBCPaKco5svFUQFMP2dso3O+qcC4k9FsKc0KxMQ==",
441 | "dependencies": {
442 | "@react-spring/shared": "~9.6.1",
443 | "@react-spring/types": "~9.6.1"
444 | },
445 | "peerDependencies": {
446 | "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
447 | }
448 | },
449 | "node_modules/@react-spring/core": {
450 | "version": "9.6.1",
451 | "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.6.1.tgz",
452 | "integrity": "sha512-3HAAinAyCPessyQNNXe5W0OHzRfa8Yo5P748paPcmMowZ/4sMfaZ2ZB6e5x5khQI8NusOHj8nquoutd6FRY5WQ==",
453 | "dependencies": {
454 | "@react-spring/animated": "~9.6.1",
455 | "@react-spring/rafz": "~9.6.1",
456 | "@react-spring/shared": "~9.6.1",
457 | "@react-spring/types": "~9.6.1"
458 | },
459 | "funding": {
460 | "type": "opencollective",
461 | "url": "https://opencollective.com/react-spring/donate"
462 | },
463 | "peerDependencies": {
464 | "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
465 | }
466 | },
467 | "node_modules/@react-spring/rafz": {
468 | "version": "9.6.1",
469 | "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.6.1.tgz",
470 | "integrity": "sha512-v6qbgNRpztJFFfSE3e2W1Uz+g8KnIBs6SmzCzcVVF61GdGfGOuBrbjIcp+nUz301awVmREKi4eMQb2Ab2gGgyQ=="
471 | },
472 | "node_modules/@react-spring/shared": {
473 | "version": "9.6.1",
474 | "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.6.1.tgz",
475 | "integrity": "sha512-PBFBXabxFEuF8enNLkVqMC9h5uLRBo6GQhRMQT/nRTnemVENimgRd+0ZT4yFnAQ0AxWNiJfX3qux+bW2LbG6Bw==",
476 | "dependencies": {
477 | "@react-spring/rafz": "~9.6.1",
478 | "@react-spring/types": "~9.6.1"
479 | },
480 | "peerDependencies": {
481 | "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
482 | }
483 | },
484 | "node_modules/@react-spring/three": {
485 | "version": "9.6.1",
486 | "resolved": "https://registry.npmjs.org/@react-spring/three/-/three-9.6.1.tgz",
487 | "integrity": "sha512-Tyw2YhZPKJAX3t2FcqvpLRb71CyTe1GvT3V+i+xJzfALgpk10uPGdGaQQ5Xrzmok1340DAeg2pR/MCfaW7b8AA==",
488 | "dependencies": {
489 | "@react-spring/animated": "~9.6.1",
490 | "@react-spring/core": "~9.6.1",
491 | "@react-spring/shared": "~9.6.1",
492 | "@react-spring/types": "~9.6.1"
493 | },
494 | "peerDependencies": {
495 | "@react-three/fiber": ">=6.0",
496 | "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
497 | "three": ">=0.126"
498 | }
499 | },
500 | "node_modules/@react-spring/types": {
501 | "version": "9.6.1",
502 | "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.6.1.tgz",
503 | "integrity": "sha512-POu8Mk0hIU3lRXB3bGIGe4VHIwwDsQyoD1F394OK7STTiX9w4dG3cTLljjYswkQN+hDSHRrj4O36kuVa7KPU8Q=="
504 | },
505 | "node_modules/@react-three/drei": {
506 | "version": "9.66.1",
507 | "resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-9.66.1.tgz",
508 | "integrity": "sha512-nbSMsdoL52bLZtOZFMjkjjGzLLkx8hOuYEgvxX19TxR9+YpFnjbfzC3NtsjK719O4OJdNEHZOxmmxoANJJAXEA==",
509 | "dependencies": {
510 | "@babel/runtime": "^7.11.2",
511 | "@react-spring/three": "~9.6.1",
512 | "@use-gesture/react": "^10.2.24",
513 | "camera-controls": "^2.3.1",
514 | "detect-gpu": "^5.0.14",
515 | "glsl-noise": "^0.0.0",
516 | "lodash.clamp": "^4.0.3",
517 | "lodash.omit": "^4.5.0",
518 | "lodash.pick": "^4.4.0",
519 | "maath": "^0.5.2",
520 | "meshline": "^3.1.6",
521 | "react-composer": "^5.0.3",
522 | "react-merge-refs": "^1.1.0",
523 | "stats.js": "^0.17.0",
524 | "suspend-react": "^0.0.8",
525 | "three-mesh-bvh": "^0.5.23",
526 | "three-stdlib": "^2.21.8",
527 | "troika-three-text": "^0.47.1",
528 | "utility-types": "^3.10.0",
529 | "zustand": "^3.5.13"
530 | },
531 | "peerDependencies": {
532 | "@react-three/fiber": ">=8.0",
533 | "react": ">=18.0",
534 | "react-dom": ">=18.0",
535 | "three": ">=0.137"
536 | },
537 | "peerDependenciesMeta": {
538 | "react-dom": {
539 | "optional": true
540 | }
541 | }
542 | },
543 | "node_modules/@react-three/drei/node_modules/zustand": {
544 | "version": "3.7.2",
545 | "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
546 | "integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==",
547 | "engines": {
548 | "node": ">=12.7.0"
549 | },
550 | "peerDependencies": {
551 | "react": ">=16.8"
552 | },
553 | "peerDependenciesMeta": {
554 | "react": {
555 | "optional": true
556 | }
557 | }
558 | },
559 | "node_modules/@react-three/fiber": {
560 | "version": "8.13.0",
561 | "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-8.13.0.tgz",
562 | "integrity": "sha512-hPFzFNgikEMyEbL+NpSA7q+UWZxInrrkJldWaCR2w34Fwf20x9p68bsyN0/yn9oM2VlWoJcJjR8hw1tN9AxHuA==",
563 | "dependencies": {
564 | "@babel/runtime": "^7.17.8",
565 | "@types/react-reconciler": "^0.26.7",
566 | "its-fine": "^1.0.6",
567 | "react-reconciler": "^0.27.0",
568 | "react-use-measure": "^2.1.1",
569 | "scheduler": "^0.21.0",
570 | "suspend-react": "^0.0.8",
571 | "zustand": "^3.7.1"
572 | },
573 | "peerDependencies": {
574 | "expo": ">=43.0",
575 | "expo-asset": ">=8.4",
576 | "expo-gl": ">=11.0",
577 | "react": ">=18.0",
578 | "react-dom": ">=18.0",
579 | "react-native": ">=0.64",
580 | "three": ">=0.133"
581 | },
582 | "peerDependenciesMeta": {
583 | "expo": {
584 | "optional": true
585 | },
586 | "expo-asset": {
587 | "optional": true
588 | },
589 | "expo-gl": {
590 | "optional": true
591 | },
592 | "react-dom": {
593 | "optional": true
594 | },
595 | "react-native": {
596 | "optional": true
597 | }
598 | }
599 | },
600 | "node_modules/@react-three/fiber/node_modules/scheduler": {
601 | "version": "0.21.0",
602 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz",
603 | "integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==",
604 | "dependencies": {
605 | "loose-envify": "^1.1.0"
606 | }
607 | },
608 | "node_modules/@react-three/fiber/node_modules/zustand": {
609 | "version": "3.7.2",
610 | "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
611 | "integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==",
612 | "engines": {
613 | "node": ">=12.7.0"
614 | },
615 | "peerDependencies": {
616 | "react": ">=16.8"
617 | },
618 | "peerDependenciesMeta": {
619 | "react": {
620 | "optional": true
621 | }
622 | }
623 | },
624 | "node_modules/@swc/core": {
625 | "version": "1.3.55",
626 | "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.55.tgz",
627 | "integrity": "sha512-w/lN3OuJsuy868yJZKop+voZLVzI5pVSoopQVtgDNkEzejnPuRp9XaeAValvuMaWqKoTMtOjLzEPyv/xiAGYQQ==",
628 | "dev": true,
629 | "hasInstallScript": true,
630 | "engines": {
631 | "node": ">=10"
632 | },
633 | "funding": {
634 | "type": "opencollective",
635 | "url": "https://opencollective.com/swc"
636 | },
637 | "optionalDependencies": {
638 | "@swc/core-darwin-arm64": "1.3.55",
639 | "@swc/core-darwin-x64": "1.3.55",
640 | "@swc/core-linux-arm-gnueabihf": "1.3.55",
641 | "@swc/core-linux-arm64-gnu": "1.3.55",
642 | "@swc/core-linux-arm64-musl": "1.3.55",
643 | "@swc/core-linux-x64-gnu": "1.3.55",
644 | "@swc/core-linux-x64-musl": "1.3.55",
645 | "@swc/core-win32-arm64-msvc": "1.3.55",
646 | "@swc/core-win32-ia32-msvc": "1.3.55",
647 | "@swc/core-win32-x64-msvc": "1.3.55"
648 | },
649 | "peerDependencies": {
650 | "@swc/helpers": "^0.5.0"
651 | },
652 | "peerDependenciesMeta": {
653 | "@swc/helpers": {
654 | "optional": true
655 | }
656 | }
657 | },
658 | "node_modules/@swc/core-darwin-arm64": {
659 | "version": "1.3.55",
660 | "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.55.tgz",
661 | "integrity": "sha512-UnHC8aPg/JvHhgXxTU6EhTtfnYNS7nhq8EKB8laNPxlHbwEyMBVQ2QuJHlNCtFtvSfX/uH5l04Ld1iGXnBTfdQ==",
662 | "cpu": [
663 | "arm64"
664 | ],
665 | "dev": true,
666 | "optional": true,
667 | "os": [
668 | "darwin"
669 | ],
670 | "engines": {
671 | "node": ">=10"
672 | }
673 | },
674 | "node_modules/@swc/core-darwin-x64": {
675 | "version": "1.3.55",
676 | "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.55.tgz",
677 | "integrity": "sha512-VNJkFVARrktIqtaLrD1NFA54gqekH7eAUcUY2U2SdHwO67HYjfMXMxlugLP5PDasSKpTkrVooUdhkffoA5W50g==",
678 | "cpu": [
679 | "x64"
680 | ],
681 | "dev": true,
682 | "optional": true,
683 | "os": [
684 | "darwin"
685 | ],
686 | "engines": {
687 | "node": ">=10"
688 | }
689 | },
690 | "node_modules/@swc/core-linux-arm-gnueabihf": {
691 | "version": "1.3.55",
692 | "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.55.tgz",
693 | "integrity": "sha512-6OcohhIFKKNW/TpJt26Tpul8zyL7dmp1Lnyj2BX9ycsZZ5UnsNiGqn37mrqJgVTx/ansEmbyOmKu2mzm/Ct6cQ==",
694 | "cpu": [
695 | "arm"
696 | ],
697 | "dev": true,
698 | "optional": true,
699 | "os": [
700 | "linux"
701 | ],
702 | "engines": {
703 | "node": ">=10"
704 | }
705 | },
706 | "node_modules/@swc/core-linux-arm64-gnu": {
707 | "version": "1.3.55",
708 | "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.55.tgz",
709 | "integrity": "sha512-MfZtXGBv21XWwvrSMP0CMxScDolT/iv5PRl9UBprYUehwWr7BNjA3V9W7QQ+kKoPyORWk7LX7OpJZF3FnO618Q==",
710 | "cpu": [
711 | "arm64"
712 | ],
713 | "dev": true,
714 | "optional": true,
715 | "os": [
716 | "linux"
717 | ],
718 | "engines": {
719 | "node": ">=10"
720 | }
721 | },
722 | "node_modules/@swc/core-linux-arm64-musl": {
723 | "version": "1.3.55",
724 | "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.55.tgz",
725 | "integrity": "sha512-iZJo+7L5lv10W0f0C6SlyteAyMJt5Tp+aH3+nlAwKdtc+VjyL1sGhR8DJMXp2/buBRZJ9tjEtpXKDaWUdSdF7Q==",
726 | "cpu": [
727 | "arm64"
728 | ],
729 | "dev": true,
730 | "optional": true,
731 | "os": [
732 | "linux"
733 | ],
734 | "engines": {
735 | "node": ">=10"
736 | }
737 | },
738 | "node_modules/@swc/core-linux-x64-gnu": {
739 | "version": "1.3.55",
740 | "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.55.tgz",
741 | "integrity": "sha512-Rmc8ny/mslzzz0+wNK9/mLdyAWVbMZHRSvljhpzASmq48NBkmZ5vk9/WID6MnUz2e9cQ0JxJQs8t39KlFJtW3g==",
742 | "cpu": [
743 | "x64"
744 | ],
745 | "dev": true,
746 | "optional": true,
747 | "os": [
748 | "linux"
749 | ],
750 | "engines": {
751 | "node": ">=10"
752 | }
753 | },
754 | "node_modules/@swc/core-linux-x64-musl": {
755 | "version": "1.3.55",
756 | "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.55.tgz",
757 | "integrity": "sha512-Ymoc4xxINzS93ZjVd2UZfLZk1jF6wHjdCbC1JF+0zK3IrNrxCIDoWoaAj0+Bbvyo3hD1Xg/cneSTsqX8amnnuQ==",
758 | "cpu": [
759 | "x64"
760 | ],
761 | "dev": true,
762 | "optional": true,
763 | "os": [
764 | "linux"
765 | ],
766 | "engines": {
767 | "node": ">=10"
768 | }
769 | },
770 | "node_modules/@swc/core-win32-arm64-msvc": {
771 | "version": "1.3.55",
772 | "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.55.tgz",
773 | "integrity": "sha512-OhnmFstq2qRU2GI5I0G/8L+vc2rx8+w+IOA6EZBrY4FuMCbPIZKKzlnAIxYn2W+yD4gvBzYP3tgEcaDfQk6EkA==",
774 | "cpu": [
775 | "arm64"
776 | ],
777 | "dev": true,
778 | "optional": true,
779 | "os": [
780 | "win32"
781 | ],
782 | "engines": {
783 | "node": ">=10"
784 | }
785 | },
786 | "node_modules/@swc/core-win32-ia32-msvc": {
787 | "version": "1.3.55",
788 | "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.55.tgz",
789 | "integrity": "sha512-3VR5rHZ6uoL/Vo3djV30GgX2oyDwWWsk+Yp+nyvYyBaKYiH2zeHfxdYRLSQV3W7kSlCAH3oDYpSljrWZ0t5XEQ==",
790 | "cpu": [
791 | "ia32"
792 | ],
793 | "dev": true,
794 | "optional": true,
795 | "os": [
796 | "win32"
797 | ],
798 | "engines": {
799 | "node": ">=10"
800 | }
801 | },
802 | "node_modules/@swc/core-win32-x64-msvc": {
803 | "version": "1.3.55",
804 | "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.55.tgz",
805 | "integrity": "sha512-KBtMFtRwnbxBugYf6i2ePqEGdxsk715KcqGMjGhxNg7BTACnXnhj37irHu2e7A7wZffbkUVUYuj/JEgVkEjSxg==",
806 | "cpu": [
807 | "x64"
808 | ],
809 | "dev": true,
810 | "optional": true,
811 | "os": [
812 | "win32"
813 | ],
814 | "engines": {
815 | "node": ">=10"
816 | }
817 | },
818 | "node_modules/@types/offscreencanvas": {
819 | "version": "2019.7.0",
820 | "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.0.tgz",
821 | "integrity": "sha512-PGcyveRIpL1XIqK8eBsmRBt76eFgtzuPiSTyKHZxnGemp2yzGzWpjYKAfK3wIMiU7eH+851yEpiuP8JZerTmWg=="
822 | },
823 | "node_modules/@types/prop-types": {
824 | "version": "15.7.5",
825 | "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
826 | "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="
827 | },
828 | "node_modules/@types/react": {
829 | "version": "18.2.0",
830 | "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.0.tgz",
831 | "integrity": "sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA==",
832 | "dependencies": {
833 | "@types/prop-types": "*",
834 | "@types/scheduler": "*",
835 | "csstype": "^3.0.2"
836 | }
837 | },
838 | "node_modules/@types/react-dom": {
839 | "version": "18.2.1",
840 | "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.1.tgz",
841 | "integrity": "sha512-8QZEV9+Kwy7tXFmjJrp3XUKQSs9LTnE0KnoUb0YCguWBiNW0Yfb2iBMYZ08WPg35IR6P3Z0s00B15SwZnO26+w==",
842 | "dev": true,
843 | "dependencies": {
844 | "@types/react": "*"
845 | }
846 | },
847 | "node_modules/@types/react-reconciler": {
848 | "version": "0.26.7",
849 | "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.26.7.tgz",
850 | "integrity": "sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ==",
851 | "dependencies": {
852 | "@types/react": "*"
853 | }
854 | },
855 | "node_modules/@types/scheduler": {
856 | "version": "0.16.3",
857 | "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
858 | "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ=="
859 | },
860 | "node_modules/@types/stats.js": {
861 | "version": "0.17.0",
862 | "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.0.tgz",
863 | "integrity": "sha512-9w+a7bR8PeB0dCT/HBULU2fMqf6BAzvKbxFboYhmDtDkKPiyXYbjoe2auwsXlEFI7CFNMF1dCv3dFH5Poy9R1w==",
864 | "peer": true
865 | },
866 | "node_modules/@types/three": {
867 | "version": "0.151.0",
868 | "resolved": "https://registry.npmjs.org/@types/three/-/three-0.151.0.tgz",
869 | "integrity": "sha512-hwOyN4szaikHsNzqbYLBfGvXi+TA/1TIKvK3ujJi+cQXzYzE5ZymzlDnOA6/cTSozY1juPFE3u5agToVQKb9TQ==",
870 | "peer": true,
871 | "dependencies": {
872 | "@types/stats.js": "*",
873 | "@types/webxr": "*",
874 | "fflate": "~0.6.9",
875 | "lil-gui": "~0.17.0"
876 | }
877 | },
878 | "node_modules/@types/webxr": {
879 | "version": "0.5.1",
880 | "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.1.tgz",
881 | "integrity": "sha512-xlFXPfgJR5vIuDefhaHuUM9uUgvPaXB6GKdXy2gdEh8gBWQZ2ul24AJz3foUd8NNKlSTQuWYJpCb1/pL81m1KQ==",
882 | "peer": true
883 | },
884 | "node_modules/@use-gesture/core": {
885 | "version": "10.2.26",
886 | "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.2.26.tgz",
887 | "integrity": "sha512-NyFpQ3iID9iFBROXyyvU1D0NK+t+dP+WAVByhCvqHUenpxLD2NlRLVRpoK3XGGwksr6mU3PvZ2Nm4q0q+gLJPA=="
888 | },
889 | "node_modules/@use-gesture/react": {
890 | "version": "10.2.26",
891 | "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.2.26.tgz",
892 | "integrity": "sha512-0QhaE5mhaQbFlip4MX7n1nwCX8gax6Da1LsP2fZ/BU6xW9zyEmV6NX7DPelDxq1rr2NiBJh30vx9RIp80YeA/A==",
893 | "dependencies": {
894 | "@use-gesture/core": "10.2.26"
895 | },
896 | "peerDependencies": {
897 | "react": ">= 16.8.0"
898 | }
899 | },
900 | "node_modules/@vitejs/plugin-react-swc": {
901 | "version": "3.3.0",
902 | "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.3.0.tgz",
903 | "integrity": "sha512-Ycg+n2eyCOTpn/wRy+evVo859+hw7qCj9iaX5CMny6x1fx1Uoq0xBG+a98lFtwLNGfGEnpI0F26YigRuxCRkwg==",
904 | "dev": true,
905 | "dependencies": {
906 | "@swc/core": "^1.3.42"
907 | },
908 | "peerDependencies": {
909 | "vite": "^4"
910 | }
911 | },
912 | "node_modules/@webgpu/glslang": {
913 | "version": "0.0.15",
914 | "resolved": "https://registry.npmjs.org/@webgpu/glslang/-/glslang-0.0.15.tgz",
915 | "integrity": "sha512-niT+Prh3Aff8Uf1MVBVUsaNjFj9rJAKDXuoHIKiQbB+6IUP/3J3JIhBNyZ7lDhytvXxw6ppgnwKZdDJ08UMj4Q=="
916 | },
917 | "node_modules/bidi-js": {
918 | "version": "1.0.2",
919 | "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.2.tgz",
920 | "integrity": "sha512-rzSy/k7WdX5zOyeHHCOixGXbCHkyogkxPKL2r8QtzHmVQDiWCXUWa18bLdMWT9CYMLOYTjWpTHawuev2ouYJVw==",
921 | "dependencies": {
922 | "require-from-string": "^2.0.2"
923 | }
924 | },
925 | "node_modules/camera-controls": {
926 | "version": "2.3.4",
927 | "resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-2.3.4.tgz",
928 | "integrity": "sha512-swhc87YVHf9te0glBI7Oa/QBgsSCL4ZxtoR4V3vE6l7mEebsYRNL8y7Y2m2E6MrT0UTphM1S7mQqs0Sp7QTZ2g==",
929 | "peerDependencies": {
930 | "three": ">=0.126.1"
931 | }
932 | },
933 | "node_modules/chevrotain": {
934 | "version": "10.5.0",
935 | "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-10.5.0.tgz",
936 | "integrity": "sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==",
937 | "dependencies": {
938 | "@chevrotain/cst-dts-gen": "10.5.0",
939 | "@chevrotain/gast": "10.5.0",
940 | "@chevrotain/types": "10.5.0",
941 | "@chevrotain/utils": "10.5.0",
942 | "lodash": "4.17.21",
943 | "regexp-to-ast": "0.5.0"
944 | }
945 | },
946 | "node_modules/control-kit": {
947 | "resolved": "..",
948 | "link": true
949 | },
950 | "node_modules/csstype": {
951 | "version": "3.1.2",
952 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
953 | "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
954 | },
955 | "node_modules/debounce": {
956 | "version": "1.2.1",
957 | "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
958 | "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
959 | },
960 | "node_modules/detect-gpu": {
961 | "version": "5.0.21",
962 | "resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.21.tgz",
963 | "integrity": "sha512-Dcf+4zxB3/4+7nVje3zQffJ3ksS7DiUztlNR0jC4AUF8FTBTeKWoIRxRAq4dGaRft8hEadUIDbjZbiKhCFo/Bw==",
964 | "dependencies": {
965 | "webgl-constants": "^1.1.1"
966 | }
967 | },
968 | "node_modules/draco3d": {
969 | "version": "1.5.6",
970 | "resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.6.tgz",
971 | "integrity": "sha512-+3NaRjWktb5r61ZFoDejlykPEFKT5N/LkbXsaddlw6xNSXBanUYpFc2AXXpbJDilPHazcSreU/DpQIaxfX0NfQ=="
972 | },
973 | "node_modules/esbuild": {
974 | "version": "0.17.18",
975 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.18.tgz",
976 | "integrity": "sha512-z1lix43jBs6UKjcZVKOw2xx69ffE2aG0PygLL5qJ9OS/gy0Ewd1gW/PUQIOIQGXBHWNywSc0floSKoMFF8aK2w==",
977 | "dev": true,
978 | "hasInstallScript": true,
979 | "bin": {
980 | "esbuild": "bin/esbuild"
981 | },
982 | "engines": {
983 | "node": ">=12"
984 | },
985 | "optionalDependencies": {
986 | "@esbuild/android-arm": "0.17.18",
987 | "@esbuild/android-arm64": "0.17.18",
988 | "@esbuild/android-x64": "0.17.18",
989 | "@esbuild/darwin-arm64": "0.17.18",
990 | "@esbuild/darwin-x64": "0.17.18",
991 | "@esbuild/freebsd-arm64": "0.17.18",
992 | "@esbuild/freebsd-x64": "0.17.18",
993 | "@esbuild/linux-arm": "0.17.18",
994 | "@esbuild/linux-arm64": "0.17.18",
995 | "@esbuild/linux-ia32": "0.17.18",
996 | "@esbuild/linux-loong64": "0.17.18",
997 | "@esbuild/linux-mips64el": "0.17.18",
998 | "@esbuild/linux-ppc64": "0.17.18",
999 | "@esbuild/linux-riscv64": "0.17.18",
1000 | "@esbuild/linux-s390x": "0.17.18",
1001 | "@esbuild/linux-x64": "0.17.18",
1002 | "@esbuild/netbsd-x64": "0.17.18",
1003 | "@esbuild/openbsd-x64": "0.17.18",
1004 | "@esbuild/sunos-x64": "0.17.18",
1005 | "@esbuild/win32-arm64": "0.17.18",
1006 | "@esbuild/win32-ia32": "0.17.18",
1007 | "@esbuild/win32-x64": "0.17.18"
1008 | }
1009 | },
1010 | "node_modules/fflate": {
1011 | "version": "0.6.10",
1012 | "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.6.10.tgz",
1013 | "integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg=="
1014 | },
1015 | "node_modules/fsevents": {
1016 | "version": "2.3.2",
1017 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
1018 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
1019 | "dev": true,
1020 | "hasInstallScript": true,
1021 | "optional": true,
1022 | "os": [
1023 | "darwin"
1024 | ],
1025 | "engines": {
1026 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
1027 | }
1028 | },
1029 | "node_modules/glsl-noise": {
1030 | "version": "0.0.0",
1031 | "resolved": "https://registry.npmjs.org/glsl-noise/-/glsl-noise-0.0.0.tgz",
1032 | "integrity": "sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w=="
1033 | },
1034 | "node_modules/its-fine": {
1035 | "version": "1.1.1",
1036 | "resolved": "https://registry.npmjs.org/its-fine/-/its-fine-1.1.1.tgz",
1037 | "integrity": "sha512-v1Ia1xl20KbuSGlwoaGsW0oxsw8Be+TrXweidxD9oT/1lAh6O3K3/GIM95Tt6WCiv6W+h2M7RB1TwdoAjQyyKw==",
1038 | "dependencies": {
1039 | "@types/react-reconciler": "^0.28.0"
1040 | },
1041 | "peerDependencies": {
1042 | "react": ">=18.0"
1043 | }
1044 | },
1045 | "node_modules/its-fine/node_modules/@types/react-reconciler": {
1046 | "version": "0.28.2",
1047 | "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.2.tgz",
1048 | "integrity": "sha512-8tu6lHzEgYPlfDf/J6GOQdIc+gs+S2yAqlby3zTsB3SP2svlqTYe5fwZNtZyfactP74ShooP2vvi1BOp9ZemWw==",
1049 | "dependencies": {
1050 | "@types/react": "*"
1051 | }
1052 | },
1053 | "node_modules/js-tokens": {
1054 | "version": "4.0.0",
1055 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
1056 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
1057 | },
1058 | "node_modules/ktx-parse": {
1059 | "version": "0.4.5",
1060 | "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-0.4.5.tgz",
1061 | "integrity": "sha512-MK3FOody4TXbFf8Yqv7EBbySw7aPvEcPX++Ipt6Sox+/YMFvR5xaTyhfNSk1AEmMy+RYIw81ctN4IMxCB8OAlg=="
1062 | },
1063 | "node_modules/lil-gui": {
1064 | "version": "0.17.0",
1065 | "resolved": "https://registry.npmjs.org/lil-gui/-/lil-gui-0.17.0.tgz",
1066 | "integrity": "sha512-MVBHmgY+uEbmJNApAaPbtvNh1RCAeMnKym82SBjtp5rODTYKWtM+MXHCifLe2H2Ti1HuBGBtK/5SyG4ShQ3pUQ==",
1067 | "peer": true
1068 | },
1069 | "node_modules/lodash": {
1070 | "version": "4.17.21",
1071 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
1072 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
1073 | },
1074 | "node_modules/lodash.clamp": {
1075 | "version": "4.0.3",
1076 | "resolved": "https://registry.npmjs.org/lodash.clamp/-/lodash.clamp-4.0.3.tgz",
1077 | "integrity": "sha512-HvzRFWjtcguTW7yd8NJBshuNaCa8aqNFtnswdT7f/cMd/1YKy5Zzoq4W/Oxvnx9l7aeY258uSdDfM793+eLsVg=="
1078 | },
1079 | "node_modules/lodash.omit": {
1080 | "version": "4.5.0",
1081 | "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz",
1082 | "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg=="
1083 | },
1084 | "node_modules/lodash.pick": {
1085 | "version": "4.4.0",
1086 | "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
1087 | "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q=="
1088 | },
1089 | "node_modules/loose-envify": {
1090 | "version": "1.4.0",
1091 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
1092 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
1093 | "dependencies": {
1094 | "js-tokens": "^3.0.0 || ^4.0.0"
1095 | },
1096 | "bin": {
1097 | "loose-envify": "cli.js"
1098 | }
1099 | },
1100 | "node_modules/maath": {
1101 | "version": "0.5.3",
1102 | "resolved": "https://registry.npmjs.org/maath/-/maath-0.5.3.tgz",
1103 | "integrity": "sha512-ut63A4zTd9abtpi+sOHW1fPWPtAFrjK0E17eAthx1k93W/T2cWLKV5oaswyotJVDvvW1EXSdokAqhK5KOu0Qdw==",
1104 | "peerDependencies": {
1105 | "@types/three": ">=0.144.0",
1106 | "three": ">=0.144.0"
1107 | }
1108 | },
1109 | "node_modules/meshline": {
1110 | "version": "3.1.6",
1111 | "resolved": "https://registry.npmjs.org/meshline/-/meshline-3.1.6.tgz",
1112 | "integrity": "sha512-8JZJOdaL5oz3PI/upG8JvP/5FfzYUOhrkJ8np/WKvXzl0/PZ2V9pqTvCIjSKv+w9ccg2xb+yyBhXAwt6ier3ug==",
1113 | "peerDependencies": {
1114 | "three": ">=0.137"
1115 | }
1116 | },
1117 | "node_modules/mmd-parser": {
1118 | "version": "1.0.4",
1119 | "resolved": "https://registry.npmjs.org/mmd-parser/-/mmd-parser-1.0.4.tgz",
1120 | "integrity": "sha512-Qi0VCU46t2IwfGv5KF0+D/t9cizcDug7qnNoy9Ggk7aucp0tssV8IwTMkBlDbm+VqAf3cdQHTCARKSsuS2MYFg=="
1121 | },
1122 | "node_modules/nanoid": {
1123 | "version": "3.3.6",
1124 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
1125 | "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
1126 | "dev": true,
1127 | "funding": [
1128 | {
1129 | "type": "github",
1130 | "url": "https://github.com/sponsors/ai"
1131 | }
1132 | ],
1133 | "bin": {
1134 | "nanoid": "bin/nanoid.cjs"
1135 | },
1136 | "engines": {
1137 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
1138 | }
1139 | },
1140 | "node_modules/object-assign": {
1141 | "version": "4.1.1",
1142 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
1143 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
1144 | "engines": {
1145 | "node": ">=0.10.0"
1146 | }
1147 | },
1148 | "node_modules/opentype.js": {
1149 | "version": "1.3.4",
1150 | "resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-1.3.4.tgz",
1151 | "integrity": "sha512-d2JE9RP/6uagpQAVtJoF0pJJA/fgai89Cc50Yp0EJHk+eLp6QQ7gBoblsnubRULNY132I0J1QKMJ+JTbMqz4sw==",
1152 | "dependencies": {
1153 | "string.prototype.codepointat": "^0.2.1",
1154 | "tiny-inflate": "^1.0.3"
1155 | },
1156 | "bin": {
1157 | "ot": "bin/ot"
1158 | },
1159 | "engines": {
1160 | "node": ">= 8.0.0"
1161 | }
1162 | },
1163 | "node_modules/picocolors": {
1164 | "version": "1.0.0",
1165 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
1166 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
1167 | "dev": true
1168 | },
1169 | "node_modules/postcss": {
1170 | "version": "8.4.23",
1171 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz",
1172 | "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==",
1173 | "dev": true,
1174 | "funding": [
1175 | {
1176 | "type": "opencollective",
1177 | "url": "https://opencollective.com/postcss/"
1178 | },
1179 | {
1180 | "type": "tidelift",
1181 | "url": "https://tidelift.com/funding/github/npm/postcss"
1182 | },
1183 | {
1184 | "type": "github",
1185 | "url": "https://github.com/sponsors/ai"
1186 | }
1187 | ],
1188 | "dependencies": {
1189 | "nanoid": "^3.3.6",
1190 | "picocolors": "^1.0.0",
1191 | "source-map-js": "^1.0.2"
1192 | },
1193 | "engines": {
1194 | "node": "^10 || ^12 || >=14"
1195 | }
1196 | },
1197 | "node_modules/potpack": {
1198 | "version": "1.0.2",
1199 | "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz",
1200 | "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ=="
1201 | },
1202 | "node_modules/prop-types": {
1203 | "version": "15.8.1",
1204 | "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
1205 | "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
1206 | "dependencies": {
1207 | "loose-envify": "^1.4.0",
1208 | "object-assign": "^4.1.1",
1209 | "react-is": "^16.13.1"
1210 | }
1211 | },
1212 | "node_modules/react": {
1213 | "version": "18.2.0",
1214 | "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
1215 | "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
1216 | "dependencies": {
1217 | "loose-envify": "^1.1.0"
1218 | },
1219 | "engines": {
1220 | "node": ">=0.10.0"
1221 | }
1222 | },
1223 | "node_modules/react-composer": {
1224 | "version": "5.0.3",
1225 | "resolved": "https://registry.npmjs.org/react-composer/-/react-composer-5.0.3.tgz",
1226 | "integrity": "sha512-1uWd07EME6XZvMfapwZmc7NgCZqDemcvicRi3wMJzXsQLvZ3L7fTHVyPy1bZdnWXM4iPjYuNE+uJ41MLKeTtnA==",
1227 | "dependencies": {
1228 | "prop-types": "^15.6.0"
1229 | },
1230 | "peerDependencies": {
1231 | "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
1232 | }
1233 | },
1234 | "node_modules/react-dom": {
1235 | "version": "18.2.0",
1236 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
1237 | "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
1238 | "dependencies": {
1239 | "loose-envify": "^1.1.0",
1240 | "scheduler": "^0.23.0"
1241 | },
1242 | "peerDependencies": {
1243 | "react": "^18.2.0"
1244 | }
1245 | },
1246 | "node_modules/react-is": {
1247 | "version": "16.13.1",
1248 | "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
1249 | "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
1250 | },
1251 | "node_modules/react-merge-refs": {
1252 | "version": "1.1.0",
1253 | "resolved": "https://registry.npmjs.org/react-merge-refs/-/react-merge-refs-1.1.0.tgz",
1254 | "integrity": "sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ==",
1255 | "funding": {
1256 | "type": "github",
1257 | "url": "https://github.com/sponsors/gregberge"
1258 | }
1259 | },
1260 | "node_modules/react-reconciler": {
1261 | "version": "0.27.0",
1262 | "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.27.0.tgz",
1263 | "integrity": "sha512-HmMDKciQjYmBRGuuhIaKA1ba/7a+UsM5FzOZsMO2JYHt9Jh8reCb7j1eDC95NOyUlKM9KRyvdx0flBuDvYSBoA==",
1264 | "dependencies": {
1265 | "loose-envify": "^1.1.0",
1266 | "scheduler": "^0.21.0"
1267 | },
1268 | "engines": {
1269 | "node": ">=0.10.0"
1270 | },
1271 | "peerDependencies": {
1272 | "react": "^18.0.0"
1273 | }
1274 | },
1275 | "node_modules/react-reconciler/node_modules/scheduler": {
1276 | "version": "0.21.0",
1277 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz",
1278 | "integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==",
1279 | "dependencies": {
1280 | "loose-envify": "^1.1.0"
1281 | }
1282 | },
1283 | "node_modules/react-use-measure": {
1284 | "version": "2.1.1",
1285 | "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.1.tgz",
1286 | "integrity": "sha512-nocZhN26cproIiIduswYpV5y5lQpSQS1y/4KuvUCjSKmw7ZWIS/+g3aFnX3WdBkyuGUtTLif3UTqnLLhbDoQig==",
1287 | "dependencies": {
1288 | "debounce": "^1.2.1"
1289 | },
1290 | "peerDependencies": {
1291 | "react": ">=16.13",
1292 | "react-dom": ">=16.13"
1293 | }
1294 | },
1295 | "node_modules/regenerator-runtime": {
1296 | "version": "0.13.11",
1297 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
1298 | "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
1299 | },
1300 | "node_modules/regexp-to-ast": {
1301 | "version": "0.5.0",
1302 | "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz",
1303 | "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw=="
1304 | },
1305 | "node_modules/require-from-string": {
1306 | "version": "2.0.2",
1307 | "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
1308 | "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
1309 | "engines": {
1310 | "node": ">=0.10.0"
1311 | }
1312 | },
1313 | "node_modules/rollup": {
1314 | "version": "3.21.0",
1315 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.21.0.tgz",
1316 | "integrity": "sha512-ANPhVcyeHvYdQMUyCbczy33nbLzI7RzrBje4uvNiTDJGIMtlKoOStmympwr9OtS1LZxiDmE2wvxHyVhoLtf1KQ==",
1317 | "dev": true,
1318 | "bin": {
1319 | "rollup": "dist/bin/rollup"
1320 | },
1321 | "engines": {
1322 | "node": ">=14.18.0",
1323 | "npm": ">=8.0.0"
1324 | },
1325 | "optionalDependencies": {
1326 | "fsevents": "~2.3.2"
1327 | }
1328 | },
1329 | "node_modules/scheduler": {
1330 | "version": "0.23.0",
1331 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
1332 | "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
1333 | "dependencies": {
1334 | "loose-envify": "^1.1.0"
1335 | }
1336 | },
1337 | "node_modules/source-map-js": {
1338 | "version": "1.0.2",
1339 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
1340 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
1341 | "dev": true,
1342 | "engines": {
1343 | "node": ">=0.10.0"
1344 | }
1345 | },
1346 | "node_modules/stats.js": {
1347 | "version": "0.17.0",
1348 | "resolved": "https://registry.npmjs.org/stats.js/-/stats.js-0.17.0.tgz",
1349 | "integrity": "sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw=="
1350 | },
1351 | "node_modules/string.prototype.codepointat": {
1352 | "version": "0.2.1",
1353 | "resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz",
1354 | "integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg=="
1355 | },
1356 | "node_modules/suspend-react": {
1357 | "version": "0.0.8",
1358 | "resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.0.8.tgz",
1359 | "integrity": "sha512-ZC3r8Hu1y0dIThzsGw0RLZplnX9yXwfItcvaIzJc2VQVi8TGyGDlu92syMB5ulybfvGLHAI5Ghzlk23UBPF8xg==",
1360 | "peerDependencies": {
1361 | "react": ">=17.0"
1362 | }
1363 | },
1364 | "node_modules/three": {
1365 | "version": "0.152.2",
1366 | "resolved": "https://registry.npmjs.org/three/-/three-0.152.2.tgz",
1367 | "integrity": "sha512-Ff9zIpSfkkqcBcpdiFo2f35vA9ZucO+N8TNacJOqaEE6DrB0eufItVMib8bK8Pcju/ZNT6a7blE1GhTpkdsILw=="
1368 | },
1369 | "node_modules/three-mesh-bvh": {
1370 | "version": "0.5.23",
1371 | "resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.5.23.tgz",
1372 | "integrity": "sha512-nyk+MskdyDgECqkxdv57UjazqqhrMi+Al9PxJN6yFtx1CTW4r0eCQ27FtyYKY5gCIWhxjtNfWYDPVy8lzx6LkA==",
1373 | "peerDependencies": {
1374 | "three": ">= 0.123.0"
1375 | }
1376 | },
1377 | "node_modules/three-stdlib": {
1378 | "version": "2.21.10",
1379 | "resolved": "https://registry.npmjs.org/three-stdlib/-/three-stdlib-2.21.10.tgz",
1380 | "integrity": "sha512-YGkR3stmgmXSHL27+1A5dWAKKB+e63PLVkRWV/LLZ7f5+ExsqjLPm4YfBmJgkoSLgILYpQs6O7baY9gnIZlmNA==",
1381 | "dependencies": {
1382 | "@babel/runtime": "^7.16.7",
1383 | "@types/offscreencanvas": "^2019.6.4",
1384 | "@webgpu/glslang": "^0.0.15",
1385 | "chevrotain": "^10.1.2",
1386 | "draco3d": "^1.4.1",
1387 | "fflate": "^0.6.9",
1388 | "ktx-parse": "^0.4.5",
1389 | "mmd-parser": "^1.0.4",
1390 | "opentype.js": "^1.3.3",
1391 | "potpack": "^1.0.1",
1392 | "zstddec": "^0.0.2"
1393 | },
1394 | "peerDependencies": {
1395 | "three": ">=0.122.0"
1396 | }
1397 | },
1398 | "node_modules/tiny-inflate": {
1399 | "version": "1.0.3",
1400 | "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
1401 | "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="
1402 | },
1403 | "node_modules/troika-three-text": {
1404 | "version": "0.47.1",
1405 | "resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.47.1.tgz",
1406 | "integrity": "sha512-/fPRUmxCkXxyUT8k6REC/aWeFzKbNr37ivrkrplSJNb3JcBUXvVt8MT0Ac5wTUvFsYTviYWprYS4/8Laen08WA==",
1407 | "dependencies": {
1408 | "bidi-js": "^1.0.2",
1409 | "troika-three-utils": "^0.47.0",
1410 | "troika-worker-utils": "^0.47.0",
1411 | "webgl-sdf-generator": "1.1.1"
1412 | },
1413 | "peerDependencies": {
1414 | "three": ">=0.125.0"
1415 | }
1416 | },
1417 | "node_modules/troika-three-utils": {
1418 | "version": "0.47.0",
1419 | "resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.47.0.tgz",
1420 | "integrity": "sha512-yoVTQxVbpQX3a55giIwqwq6hyJA6oYvq7kaNGwFTeicoWmTZCqqTbytafx1gcuL5umrtw5MYgsxYUSOha+xp5w==",
1421 | "peerDependencies": {
1422 | "three": ">=0.125.0"
1423 | }
1424 | },
1425 | "node_modules/troika-worker-utils": {
1426 | "version": "0.47.0",
1427 | "resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.47.0.tgz",
1428 | "integrity": "sha512-PSUc9vunDEkbE23jpgXD3PcF96jQHKjgMjS+4o5g6DEK/ZAPTnldb+FNddhppawfUcuraMFrslo0GmIC8UpEmA=="
1429 | },
1430 | "node_modules/typescript": {
1431 | "version": "4.9.5",
1432 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
1433 | "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
1434 | "dev": true,
1435 | "bin": {
1436 | "tsc": "bin/tsc",
1437 | "tsserver": "bin/tsserver"
1438 | },
1439 | "engines": {
1440 | "node": ">=4.2.0"
1441 | }
1442 | },
1443 | "node_modules/use-sync-external-store": {
1444 | "version": "1.2.0",
1445 | "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
1446 | "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
1447 | "peerDependencies": {
1448 | "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
1449 | }
1450 | },
1451 | "node_modules/utility-types": {
1452 | "version": "3.10.0",
1453 | "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz",
1454 | "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==",
1455 | "engines": {
1456 | "node": ">= 4"
1457 | }
1458 | },
1459 | "node_modules/vite": {
1460 | "version": "4.3.3",
1461 | "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.3.tgz",
1462 | "integrity": "sha512-MwFlLBO4udZXd+VBcezo3u8mC77YQk+ik+fbc0GZWGgzfbPP+8Kf0fldhARqvSYmtIWoAJ5BXPClUbMTlqFxrA==",
1463 | "dev": true,
1464 | "dependencies": {
1465 | "esbuild": "^0.17.5",
1466 | "postcss": "^8.4.23",
1467 | "rollup": "^3.21.0"
1468 | },
1469 | "bin": {
1470 | "vite": "bin/vite.js"
1471 | },
1472 | "engines": {
1473 | "node": "^14.18.0 || >=16.0.0"
1474 | },
1475 | "optionalDependencies": {
1476 | "fsevents": "~2.3.2"
1477 | },
1478 | "peerDependencies": {
1479 | "@types/node": ">= 14",
1480 | "less": "*",
1481 | "sass": "*",
1482 | "stylus": "*",
1483 | "sugarss": "*",
1484 | "terser": "^5.4.0"
1485 | },
1486 | "peerDependenciesMeta": {
1487 | "@types/node": {
1488 | "optional": true
1489 | },
1490 | "less": {
1491 | "optional": true
1492 | },
1493 | "sass": {
1494 | "optional": true
1495 | },
1496 | "stylus": {
1497 | "optional": true
1498 | },
1499 | "sugarss": {
1500 | "optional": true
1501 | },
1502 | "terser": {
1503 | "optional": true
1504 | }
1505 | }
1506 | },
1507 | "node_modules/webgl-constants": {
1508 | "version": "1.1.1",
1509 | "resolved": "https://registry.npmjs.org/webgl-constants/-/webgl-constants-1.1.1.tgz",
1510 | "integrity": "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg=="
1511 | },
1512 | "node_modules/webgl-sdf-generator": {
1513 | "version": "1.1.1",
1514 | "resolved": "https://registry.npmjs.org/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz",
1515 | "integrity": "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA=="
1516 | },
1517 | "node_modules/zstddec": {
1518 | "version": "0.0.2",
1519 | "resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.0.2.tgz",
1520 | "integrity": "sha512-DCo0oxvcvOTGP/f5FA6tz2Z6wF+FIcEApSTu0zV5sQgn9hoT5lZ9YRAKUraxt9oP7l4e8TnNdi8IZTCX6WCkwA=="
1521 | },
1522 | "node_modules/zustand": {
1523 | "version": "4.3.7",
1524 | "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.3.7.tgz",
1525 | "integrity": "sha512-dY8ERwB9Nd21ellgkBZFhudER8KVlelZm8388B5nDAXhO/+FZDhYMuRnqDgu5SYyRgz/iaf8RKnbUs/cHfOGlQ==",
1526 | "dependencies": {
1527 | "use-sync-external-store": "1.2.0"
1528 | },
1529 | "engines": {
1530 | "node": ">=12.7.0"
1531 | },
1532 | "peerDependencies": {
1533 | "immer": ">=9.0",
1534 | "react": ">=16.8"
1535 | },
1536 | "peerDependenciesMeta": {
1537 | "immer": {
1538 | "optional": true
1539 | },
1540 | "react": {
1541 | "optional": true
1542 | }
1543 | }
1544 | }
1545 | },
1546 | "dependencies": {
1547 | "@babel/runtime": {
1548 | "version": "7.21.5",
1549 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz",
1550 | "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==",
1551 | "requires": {
1552 | "regenerator-runtime": "^0.13.11"
1553 | }
1554 | },
1555 | "@chevrotain/cst-dts-gen": {
1556 | "version": "10.5.0",
1557 | "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.5.0.tgz",
1558 | "integrity": "sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==",
1559 | "requires": {
1560 | "@chevrotain/gast": "10.5.0",
1561 | "@chevrotain/types": "10.5.0",
1562 | "lodash": "4.17.21"
1563 | }
1564 | },
1565 | "@chevrotain/gast": {
1566 | "version": "10.5.0",
1567 | "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-10.5.0.tgz",
1568 | "integrity": "sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==",
1569 | "requires": {
1570 | "@chevrotain/types": "10.5.0",
1571 | "lodash": "4.17.21"
1572 | }
1573 | },
1574 | "@chevrotain/types": {
1575 | "version": "10.5.0",
1576 | "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-10.5.0.tgz",
1577 | "integrity": "sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A=="
1578 | },
1579 | "@chevrotain/utils": {
1580 | "version": "10.5.0",
1581 | "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-10.5.0.tgz",
1582 | "integrity": "sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ=="
1583 | },
1584 | "@esbuild/android-arm": {
1585 | "version": "0.17.18",
1586 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.18.tgz",
1587 | "integrity": "sha512-EmwL+vUBZJ7mhFCs5lA4ZimpUH3WMAoqvOIYhVQwdIgSpHC8ImHdsRyhHAVxpDYUSm0lWvd63z0XH1IlImS2Qw==",
1588 | "dev": true,
1589 | "optional": true
1590 | },
1591 | "@esbuild/android-arm64": {
1592 | "version": "0.17.18",
1593 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.18.tgz",
1594 | "integrity": "sha512-/iq0aK0eeHgSC3z55ucMAHO05OIqmQehiGay8eP5l/5l+iEr4EIbh4/MI8xD9qRFjqzgkc0JkX0LculNC9mXBw==",
1595 | "dev": true,
1596 | "optional": true
1597 | },
1598 | "@esbuild/android-x64": {
1599 | "version": "0.17.18",
1600 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.18.tgz",
1601 | "integrity": "sha512-x+0efYNBF3NPW2Xc5bFOSFW7tTXdAcpfEg2nXmxegm4mJuVeS+i109m/7HMiOQ6M12aVGGFlqJX3RhNdYM2lWg==",
1602 | "dev": true,
1603 | "optional": true
1604 | },
1605 | "@esbuild/darwin-arm64": {
1606 | "version": "0.17.18",
1607 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.18.tgz",
1608 | "integrity": "sha512-6tY+djEAdF48M1ONWnQb1C+6LiXrKjmqjzPNPWXhu/GzOHTHX2nh8Mo2ZAmBFg0kIodHhciEgUBtcYCAIjGbjQ==",
1609 | "dev": true,
1610 | "optional": true
1611 | },
1612 | "@esbuild/darwin-x64": {
1613 | "version": "0.17.18",
1614 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.18.tgz",
1615 | "integrity": "sha512-Qq84ykvLvya3dO49wVC9FFCNUfSrQJLbxhoQk/TE1r6MjHo3sFF2tlJCwMjhkBVq3/ahUisj7+EpRSz0/+8+9A==",
1616 | "dev": true,
1617 | "optional": true
1618 | },
1619 | "@esbuild/freebsd-arm64": {
1620 | "version": "0.17.18",
1621 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.18.tgz",
1622 | "integrity": "sha512-fw/ZfxfAzuHfaQeMDhbzxp9mc+mHn1Y94VDHFHjGvt2Uxl10mT4CDavHm+/L9KG441t1QdABqkVYwakMUeyLRA==",
1623 | "dev": true,
1624 | "optional": true
1625 | },
1626 | "@esbuild/freebsd-x64": {
1627 | "version": "0.17.18",
1628 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.18.tgz",
1629 | "integrity": "sha512-FQFbRtTaEi8ZBi/A6kxOC0V0E9B/97vPdYjY9NdawyLd4Qk5VD5g2pbWN2VR1c0xhzcJm74HWpObPszWC+qTew==",
1630 | "dev": true,
1631 | "optional": true
1632 | },
1633 | "@esbuild/linux-arm": {
1634 | "version": "0.17.18",
1635 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.18.tgz",
1636 | "integrity": "sha512-jW+UCM40LzHcouIaqv3e/oRs0JM76JfhHjCavPxMUti7VAPh8CaGSlS7cmyrdpzSk7A+8f0hiedHqr/LMnfijg==",
1637 | "dev": true,
1638 | "optional": true
1639 | },
1640 | "@esbuild/linux-arm64": {
1641 | "version": "0.17.18",
1642 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.18.tgz",
1643 | "integrity": "sha512-R7pZvQZFOY2sxUG8P6A21eq6q+eBv7JPQYIybHVf1XkQYC+lT7nDBdC7wWKTrbvMXKRaGudp/dzZCwL/863mZQ==",
1644 | "dev": true,
1645 | "optional": true
1646 | },
1647 | "@esbuild/linux-ia32": {
1648 | "version": "0.17.18",
1649 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.18.tgz",
1650 | "integrity": "sha512-ygIMc3I7wxgXIxk6j3V00VlABIjq260i967Cp9BNAk5pOOpIXmd1RFQJQX9Io7KRsthDrQYrtcx7QCof4o3ZoQ==",
1651 | "dev": true,
1652 | "optional": true
1653 | },
1654 | "@esbuild/linux-loong64": {
1655 | "version": "0.17.18",
1656 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.18.tgz",
1657 | "integrity": "sha512-bvPG+MyFs5ZlwYclCG1D744oHk1Pv7j8psF5TfYx7otCVmcJsEXgFEhQkbhNW8otDHL1a2KDINW20cfCgnzgMQ==",
1658 | "dev": true,
1659 | "optional": true
1660 | },
1661 | "@esbuild/linux-mips64el": {
1662 | "version": "0.17.18",
1663 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.18.tgz",
1664 | "integrity": "sha512-oVqckATOAGuiUOa6wr8TXaVPSa+6IwVJrGidmNZS1cZVx0HqkTMkqFGD2HIx9H1RvOwFeWYdaYbdY6B89KUMxA==",
1665 | "dev": true,
1666 | "optional": true
1667 | },
1668 | "@esbuild/linux-ppc64": {
1669 | "version": "0.17.18",
1670 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.18.tgz",
1671 | "integrity": "sha512-3dLlQO+b/LnQNxgH4l9rqa2/IwRJVN9u/bK63FhOPB4xqiRqlQAU0qDU3JJuf0BmaH0yytTBdoSBHrb2jqc5qQ==",
1672 | "dev": true,
1673 | "optional": true
1674 | },
1675 | "@esbuild/linux-riscv64": {
1676 | "version": "0.17.18",
1677 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.18.tgz",
1678 | "integrity": "sha512-/x7leOyDPjZV3TcsdfrSI107zItVnsX1q2nho7hbbQoKnmoeUWjs+08rKKt4AUXju7+3aRZSsKrJtaRmsdL1xA==",
1679 | "dev": true,
1680 | "optional": true
1681 | },
1682 | "@esbuild/linux-s390x": {
1683 | "version": "0.17.18",
1684 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.18.tgz",
1685 | "integrity": "sha512-cX0I8Q9xQkL/6F5zWdYmVf5JSQt+ZfZD2bJudZrWD+4mnUvoZ3TDDXtDX2mUaq6upMFv9FlfIh4Gfun0tbGzuw==",
1686 | "dev": true,
1687 | "optional": true
1688 | },
1689 | "@esbuild/linux-x64": {
1690 | "version": "0.17.18",
1691 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.18.tgz",
1692 | "integrity": "sha512-66RmRsPlYy4jFl0vG80GcNRdirx4nVWAzJmXkevgphP1qf4dsLQCpSKGM3DUQCojwU1hnepI63gNZdrr02wHUA==",
1693 | "dev": true,
1694 | "optional": true
1695 | },
1696 | "@esbuild/netbsd-x64": {
1697 | "version": "0.17.18",
1698 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.18.tgz",
1699 | "integrity": "sha512-95IRY7mI2yrkLlTLb1gpDxdC5WLC5mZDi+kA9dmM5XAGxCME0F8i4bYH4jZreaJ6lIZ0B8hTrweqG1fUyW7jbg==",
1700 | "dev": true,
1701 | "optional": true
1702 | },
1703 | "@esbuild/openbsd-x64": {
1704 | "version": "0.17.18",
1705 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.18.tgz",
1706 | "integrity": "sha512-WevVOgcng+8hSZ4Q3BKL3n1xTv5H6Nb53cBrtzzEjDbbnOmucEVcZeGCsCOi9bAOcDYEeBZbD2SJNBxlfP3qiA==",
1707 | "dev": true,
1708 | "optional": true
1709 | },
1710 | "@esbuild/sunos-x64": {
1711 | "version": "0.17.18",
1712 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.18.tgz",
1713 | "integrity": "sha512-Rzf4QfQagnwhQXVBS3BYUlxmEbcV7MY+BH5vfDZekU5eYpcffHSyjU8T0xucKVuOcdCsMo+Ur5wmgQJH2GfNrg==",
1714 | "dev": true,
1715 | "optional": true
1716 | },
1717 | "@esbuild/win32-arm64": {
1718 | "version": "0.17.18",
1719 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.18.tgz",
1720 | "integrity": "sha512-Kb3Ko/KKaWhjeAm2YoT/cNZaHaD1Yk/pa3FTsmqo9uFh1D1Rfco7BBLIPdDOozrObj2sahslFuAQGvWbgWldAg==",
1721 | "dev": true,
1722 | "optional": true
1723 | },
1724 | "@esbuild/win32-ia32": {
1725 | "version": "0.17.18",
1726 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.18.tgz",
1727 | "integrity": "sha512-0/xUMIdkVHwkvxfbd5+lfG7mHOf2FRrxNbPiKWg9C4fFrB8H0guClmaM3BFiRUYrznVoyxTIyC/Ou2B7QQSwmw==",
1728 | "dev": true,
1729 | "optional": true
1730 | },
1731 | "@esbuild/win32-x64": {
1732 | "version": "0.17.18",
1733 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.18.tgz",
1734 | "integrity": "sha512-qU25Ma1I3NqTSHJUOKi9sAH1/Mzuvlke0ioMJRthLXKm7JiSKVwFghlGbDLOO2sARECGhja4xYfRAZNPAkooYg==",
1735 | "dev": true,
1736 | "optional": true
1737 | },
1738 | "@react-spring/animated": {
1739 | "version": "9.6.1",
1740 | "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.6.1.tgz",
1741 | "integrity": "sha512-ls/rJBrAqiAYozjLo5EPPLLOb1LM0lNVQcXODTC1SMtS6DbuBCPaKco5svFUQFMP2dso3O+qcC4k9FsKc0KxMQ==",
1742 | "requires": {
1743 | "@react-spring/shared": "~9.6.1",
1744 | "@react-spring/types": "~9.6.1"
1745 | }
1746 | },
1747 | "@react-spring/core": {
1748 | "version": "9.6.1",
1749 | "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.6.1.tgz",
1750 | "integrity": "sha512-3HAAinAyCPessyQNNXe5W0OHzRfa8Yo5P748paPcmMowZ/4sMfaZ2ZB6e5x5khQI8NusOHj8nquoutd6FRY5WQ==",
1751 | "requires": {
1752 | "@react-spring/animated": "~9.6.1",
1753 | "@react-spring/rafz": "~9.6.1",
1754 | "@react-spring/shared": "~9.6.1",
1755 | "@react-spring/types": "~9.6.1"
1756 | }
1757 | },
1758 | "@react-spring/rafz": {
1759 | "version": "9.6.1",
1760 | "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.6.1.tgz",
1761 | "integrity": "sha512-v6qbgNRpztJFFfSE3e2W1Uz+g8KnIBs6SmzCzcVVF61GdGfGOuBrbjIcp+nUz301awVmREKi4eMQb2Ab2gGgyQ=="
1762 | },
1763 | "@react-spring/shared": {
1764 | "version": "9.6.1",
1765 | "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.6.1.tgz",
1766 | "integrity": "sha512-PBFBXabxFEuF8enNLkVqMC9h5uLRBo6GQhRMQT/nRTnemVENimgRd+0ZT4yFnAQ0AxWNiJfX3qux+bW2LbG6Bw==",
1767 | "requires": {
1768 | "@react-spring/rafz": "~9.6.1",
1769 | "@react-spring/types": "~9.6.1"
1770 | }
1771 | },
1772 | "@react-spring/three": {
1773 | "version": "9.6.1",
1774 | "resolved": "https://registry.npmjs.org/@react-spring/three/-/three-9.6.1.tgz",
1775 | "integrity": "sha512-Tyw2YhZPKJAX3t2FcqvpLRb71CyTe1GvT3V+i+xJzfALgpk10uPGdGaQQ5Xrzmok1340DAeg2pR/MCfaW7b8AA==",
1776 | "requires": {
1777 | "@react-spring/animated": "~9.6.1",
1778 | "@react-spring/core": "~9.6.1",
1779 | "@react-spring/shared": "~9.6.1",
1780 | "@react-spring/types": "~9.6.1"
1781 | }
1782 | },
1783 | "@react-spring/types": {
1784 | "version": "9.6.1",
1785 | "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.6.1.tgz",
1786 | "integrity": "sha512-POu8Mk0hIU3lRXB3bGIGe4VHIwwDsQyoD1F394OK7STTiX9w4dG3cTLljjYswkQN+hDSHRrj4O36kuVa7KPU8Q=="
1787 | },
1788 | "@react-three/drei": {
1789 | "version": "9.66.1",
1790 | "resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-9.66.1.tgz",
1791 | "integrity": "sha512-nbSMsdoL52bLZtOZFMjkjjGzLLkx8hOuYEgvxX19TxR9+YpFnjbfzC3NtsjK719O4OJdNEHZOxmmxoANJJAXEA==",
1792 | "requires": {
1793 | "@babel/runtime": "^7.11.2",
1794 | "@react-spring/three": "~9.6.1",
1795 | "@use-gesture/react": "^10.2.24",
1796 | "camera-controls": "^2.3.1",
1797 | "detect-gpu": "^5.0.14",
1798 | "glsl-noise": "^0.0.0",
1799 | "lodash.clamp": "^4.0.3",
1800 | "lodash.omit": "^4.5.0",
1801 | "lodash.pick": "^4.4.0",
1802 | "maath": "^0.5.2",
1803 | "meshline": "^3.1.6",
1804 | "react-composer": "^5.0.3",
1805 | "react-merge-refs": "^1.1.0",
1806 | "stats.js": "^0.17.0",
1807 | "suspend-react": "^0.0.8",
1808 | "three-mesh-bvh": "^0.5.23",
1809 | "three-stdlib": "^2.21.8",
1810 | "troika-three-text": "^0.47.1",
1811 | "utility-types": "^3.10.0",
1812 | "zustand": "^3.5.13"
1813 | },
1814 | "dependencies": {
1815 | "zustand": {
1816 | "version": "3.7.2",
1817 | "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
1818 | "integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==",
1819 | "requires": {}
1820 | }
1821 | }
1822 | },
1823 | "@react-three/fiber": {
1824 | "version": "8.13.0",
1825 | "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-8.13.0.tgz",
1826 | "integrity": "sha512-hPFzFNgikEMyEbL+NpSA7q+UWZxInrrkJldWaCR2w34Fwf20x9p68bsyN0/yn9oM2VlWoJcJjR8hw1tN9AxHuA==",
1827 | "requires": {
1828 | "@babel/runtime": "^7.17.8",
1829 | "@types/react-reconciler": "^0.26.7",
1830 | "its-fine": "^1.0.6",
1831 | "react-reconciler": "^0.27.0",
1832 | "react-use-measure": "^2.1.1",
1833 | "scheduler": "^0.21.0",
1834 | "suspend-react": "^0.0.8",
1835 | "zustand": "^3.7.1"
1836 | },
1837 | "dependencies": {
1838 | "scheduler": {
1839 | "version": "0.21.0",
1840 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz",
1841 | "integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==",
1842 | "requires": {
1843 | "loose-envify": "^1.1.0"
1844 | }
1845 | },
1846 | "zustand": {
1847 | "version": "3.7.2",
1848 | "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
1849 | "integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==",
1850 | "requires": {}
1851 | }
1852 | }
1853 | },
1854 | "@swc/core": {
1855 | "version": "1.3.55",
1856 | "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.55.tgz",
1857 | "integrity": "sha512-w/lN3OuJsuy868yJZKop+voZLVzI5pVSoopQVtgDNkEzejnPuRp9XaeAValvuMaWqKoTMtOjLzEPyv/xiAGYQQ==",
1858 | "dev": true,
1859 | "requires": {
1860 | "@swc/core-darwin-arm64": "1.3.55",
1861 | "@swc/core-darwin-x64": "1.3.55",
1862 | "@swc/core-linux-arm-gnueabihf": "1.3.55",
1863 | "@swc/core-linux-arm64-gnu": "1.3.55",
1864 | "@swc/core-linux-arm64-musl": "1.3.55",
1865 | "@swc/core-linux-x64-gnu": "1.3.55",
1866 | "@swc/core-linux-x64-musl": "1.3.55",
1867 | "@swc/core-win32-arm64-msvc": "1.3.55",
1868 | "@swc/core-win32-ia32-msvc": "1.3.55",
1869 | "@swc/core-win32-x64-msvc": "1.3.55"
1870 | }
1871 | },
1872 | "@swc/core-darwin-arm64": {
1873 | "version": "1.3.55",
1874 | "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.55.tgz",
1875 | "integrity": "sha512-UnHC8aPg/JvHhgXxTU6EhTtfnYNS7nhq8EKB8laNPxlHbwEyMBVQ2QuJHlNCtFtvSfX/uH5l04Ld1iGXnBTfdQ==",
1876 | "dev": true,
1877 | "optional": true
1878 | },
1879 | "@swc/core-darwin-x64": {
1880 | "version": "1.3.55",
1881 | "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.55.tgz",
1882 | "integrity": "sha512-VNJkFVARrktIqtaLrD1NFA54gqekH7eAUcUY2U2SdHwO67HYjfMXMxlugLP5PDasSKpTkrVooUdhkffoA5W50g==",
1883 | "dev": true,
1884 | "optional": true
1885 | },
1886 | "@swc/core-linux-arm-gnueabihf": {
1887 | "version": "1.3.55",
1888 | "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.55.tgz",
1889 | "integrity": "sha512-6OcohhIFKKNW/TpJt26Tpul8zyL7dmp1Lnyj2BX9ycsZZ5UnsNiGqn37mrqJgVTx/ansEmbyOmKu2mzm/Ct6cQ==",
1890 | "dev": true,
1891 | "optional": true
1892 | },
1893 | "@swc/core-linux-arm64-gnu": {
1894 | "version": "1.3.55",
1895 | "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.55.tgz",
1896 | "integrity": "sha512-MfZtXGBv21XWwvrSMP0CMxScDolT/iv5PRl9UBprYUehwWr7BNjA3V9W7QQ+kKoPyORWk7LX7OpJZF3FnO618Q==",
1897 | "dev": true,
1898 | "optional": true
1899 | },
1900 | "@swc/core-linux-arm64-musl": {
1901 | "version": "1.3.55",
1902 | "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.55.tgz",
1903 | "integrity": "sha512-iZJo+7L5lv10W0f0C6SlyteAyMJt5Tp+aH3+nlAwKdtc+VjyL1sGhR8DJMXp2/buBRZJ9tjEtpXKDaWUdSdF7Q==",
1904 | "dev": true,
1905 | "optional": true
1906 | },
1907 | "@swc/core-linux-x64-gnu": {
1908 | "version": "1.3.55",
1909 | "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.55.tgz",
1910 | "integrity": "sha512-Rmc8ny/mslzzz0+wNK9/mLdyAWVbMZHRSvljhpzASmq48NBkmZ5vk9/WID6MnUz2e9cQ0JxJQs8t39KlFJtW3g==",
1911 | "dev": true,
1912 | "optional": true
1913 | },
1914 | "@swc/core-linux-x64-musl": {
1915 | "version": "1.3.55",
1916 | "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.55.tgz",
1917 | "integrity": "sha512-Ymoc4xxINzS93ZjVd2UZfLZk1jF6wHjdCbC1JF+0zK3IrNrxCIDoWoaAj0+Bbvyo3hD1Xg/cneSTsqX8amnnuQ==",
1918 | "dev": true,
1919 | "optional": true
1920 | },
1921 | "@swc/core-win32-arm64-msvc": {
1922 | "version": "1.3.55",
1923 | "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.55.tgz",
1924 | "integrity": "sha512-OhnmFstq2qRU2GI5I0G/8L+vc2rx8+w+IOA6EZBrY4FuMCbPIZKKzlnAIxYn2W+yD4gvBzYP3tgEcaDfQk6EkA==",
1925 | "dev": true,
1926 | "optional": true
1927 | },
1928 | "@swc/core-win32-ia32-msvc": {
1929 | "version": "1.3.55",
1930 | "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.55.tgz",
1931 | "integrity": "sha512-3VR5rHZ6uoL/Vo3djV30GgX2oyDwWWsk+Yp+nyvYyBaKYiH2zeHfxdYRLSQV3W7kSlCAH3oDYpSljrWZ0t5XEQ==",
1932 | "dev": true,
1933 | "optional": true
1934 | },
1935 | "@swc/core-win32-x64-msvc": {
1936 | "version": "1.3.55",
1937 | "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.55.tgz",
1938 | "integrity": "sha512-KBtMFtRwnbxBugYf6i2ePqEGdxsk715KcqGMjGhxNg7BTACnXnhj37irHu2e7A7wZffbkUVUYuj/JEgVkEjSxg==",
1939 | "dev": true,
1940 | "optional": true
1941 | },
1942 | "@types/offscreencanvas": {
1943 | "version": "2019.7.0",
1944 | "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.0.tgz",
1945 | "integrity": "sha512-PGcyveRIpL1XIqK8eBsmRBt76eFgtzuPiSTyKHZxnGemp2yzGzWpjYKAfK3wIMiU7eH+851yEpiuP8JZerTmWg=="
1946 | },
1947 | "@types/prop-types": {
1948 | "version": "15.7.5",
1949 | "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
1950 | "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="
1951 | },
1952 | "@types/react": {
1953 | "version": "18.2.0",
1954 | "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.0.tgz",
1955 | "integrity": "sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA==",
1956 | "requires": {
1957 | "@types/prop-types": "*",
1958 | "@types/scheduler": "*",
1959 | "csstype": "^3.0.2"
1960 | }
1961 | },
1962 | "@types/react-dom": {
1963 | "version": "18.2.1",
1964 | "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.1.tgz",
1965 | "integrity": "sha512-8QZEV9+Kwy7tXFmjJrp3XUKQSs9LTnE0KnoUb0YCguWBiNW0Yfb2iBMYZ08WPg35IR6P3Z0s00B15SwZnO26+w==",
1966 | "dev": true,
1967 | "requires": {
1968 | "@types/react": "*"
1969 | }
1970 | },
1971 | "@types/react-reconciler": {
1972 | "version": "0.26.7",
1973 | "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.26.7.tgz",
1974 | "integrity": "sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ==",
1975 | "requires": {
1976 | "@types/react": "*"
1977 | }
1978 | },
1979 | "@types/scheduler": {
1980 | "version": "0.16.3",
1981 | "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
1982 | "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ=="
1983 | },
1984 | "@types/stats.js": {
1985 | "version": "0.17.0",
1986 | "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.0.tgz",
1987 | "integrity": "sha512-9w+a7bR8PeB0dCT/HBULU2fMqf6BAzvKbxFboYhmDtDkKPiyXYbjoe2auwsXlEFI7CFNMF1dCv3dFH5Poy9R1w==",
1988 | "peer": true
1989 | },
1990 | "@types/three": {
1991 | "version": "0.151.0",
1992 | "resolved": "https://registry.npmjs.org/@types/three/-/three-0.151.0.tgz",
1993 | "integrity": "sha512-hwOyN4szaikHsNzqbYLBfGvXi+TA/1TIKvK3ujJi+cQXzYzE5ZymzlDnOA6/cTSozY1juPFE3u5agToVQKb9TQ==",
1994 | "peer": true,
1995 | "requires": {
1996 | "@types/stats.js": "*",
1997 | "@types/webxr": "*",
1998 | "fflate": "~0.6.9",
1999 | "lil-gui": "~0.17.0"
2000 | }
2001 | },
2002 | "@types/webxr": {
2003 | "version": "0.5.1",
2004 | "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.1.tgz",
2005 | "integrity": "sha512-xlFXPfgJR5vIuDefhaHuUM9uUgvPaXB6GKdXy2gdEh8gBWQZ2ul24AJz3foUd8NNKlSTQuWYJpCb1/pL81m1KQ==",
2006 | "peer": true
2007 | },
2008 | "@use-gesture/core": {
2009 | "version": "10.2.26",
2010 | "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.2.26.tgz",
2011 | "integrity": "sha512-NyFpQ3iID9iFBROXyyvU1D0NK+t+dP+WAVByhCvqHUenpxLD2NlRLVRpoK3XGGwksr6mU3PvZ2Nm4q0q+gLJPA=="
2012 | },
2013 | "@use-gesture/react": {
2014 | "version": "10.2.26",
2015 | "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.2.26.tgz",
2016 | "integrity": "sha512-0QhaE5mhaQbFlip4MX7n1nwCX8gax6Da1LsP2fZ/BU6xW9zyEmV6NX7DPelDxq1rr2NiBJh30vx9RIp80YeA/A==",
2017 | "requires": {
2018 | "@use-gesture/core": "10.2.26"
2019 | }
2020 | },
2021 | "@vitejs/plugin-react-swc": {
2022 | "version": "3.3.0",
2023 | "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.3.0.tgz",
2024 | "integrity": "sha512-Ycg+n2eyCOTpn/wRy+evVo859+hw7qCj9iaX5CMny6x1fx1Uoq0xBG+a98lFtwLNGfGEnpI0F26YigRuxCRkwg==",
2025 | "dev": true,
2026 | "requires": {
2027 | "@swc/core": "^1.3.42"
2028 | }
2029 | },
2030 | "@webgpu/glslang": {
2031 | "version": "0.0.15",
2032 | "resolved": "https://registry.npmjs.org/@webgpu/glslang/-/glslang-0.0.15.tgz",
2033 | "integrity": "sha512-niT+Prh3Aff8Uf1MVBVUsaNjFj9rJAKDXuoHIKiQbB+6IUP/3J3JIhBNyZ7lDhytvXxw6ppgnwKZdDJ08UMj4Q=="
2034 | },
2035 | "bidi-js": {
2036 | "version": "1.0.2",
2037 | "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.2.tgz",
2038 | "integrity": "sha512-rzSy/k7WdX5zOyeHHCOixGXbCHkyogkxPKL2r8QtzHmVQDiWCXUWa18bLdMWT9CYMLOYTjWpTHawuev2ouYJVw==",
2039 | "requires": {
2040 | "require-from-string": "^2.0.2"
2041 | }
2042 | },
2043 | "camera-controls": {
2044 | "version": "2.3.4",
2045 | "resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-2.3.4.tgz",
2046 | "integrity": "sha512-swhc87YVHf9te0glBI7Oa/QBgsSCL4ZxtoR4V3vE6l7mEebsYRNL8y7Y2m2E6MrT0UTphM1S7mQqs0Sp7QTZ2g==",
2047 | "requires": {}
2048 | },
2049 | "chevrotain": {
2050 | "version": "10.5.0",
2051 | "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-10.5.0.tgz",
2052 | "integrity": "sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==",
2053 | "requires": {
2054 | "@chevrotain/cst-dts-gen": "10.5.0",
2055 | "@chevrotain/gast": "10.5.0",
2056 | "@chevrotain/types": "10.5.0",
2057 | "@chevrotain/utils": "10.5.0",
2058 | "lodash": "4.17.21",
2059 | "regexp-to-ast": "0.5.0"
2060 | }
2061 | },
2062 | "control-kit": {
2063 | "version": "file:..",
2064 | "requires": {
2065 | "@react-three/drei": ">=9.53",
2066 | "@types/node": "^18.11.19",
2067 | "@types/react": "^18.2.0",
2068 | "@types/react-dom": "^18.2.0",
2069 | "@types/three": "^0.149.0",
2070 | "prettier-config-standard": "^5.0.0",
2071 | "three": ">=0.149",
2072 | "typescript": "^5.0.0"
2073 | }
2074 | },
2075 | "csstype": {
2076 | "version": "3.1.2",
2077 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
2078 | "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
2079 | },
2080 | "debounce": {
2081 | "version": "1.2.1",
2082 | "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
2083 | "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
2084 | },
2085 | "detect-gpu": {
2086 | "version": "5.0.21",
2087 | "resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.21.tgz",
2088 | "integrity": "sha512-Dcf+4zxB3/4+7nVje3zQffJ3ksS7DiUztlNR0jC4AUF8FTBTeKWoIRxRAq4dGaRft8hEadUIDbjZbiKhCFo/Bw==",
2089 | "requires": {
2090 | "webgl-constants": "^1.1.1"
2091 | }
2092 | },
2093 | "draco3d": {
2094 | "version": "1.5.6",
2095 | "resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.6.tgz",
2096 | "integrity": "sha512-+3NaRjWktb5r61ZFoDejlykPEFKT5N/LkbXsaddlw6xNSXBanUYpFc2AXXpbJDilPHazcSreU/DpQIaxfX0NfQ=="
2097 | },
2098 | "esbuild": {
2099 | "version": "0.17.18",
2100 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.18.tgz",
2101 | "integrity": "sha512-z1lix43jBs6UKjcZVKOw2xx69ffE2aG0PygLL5qJ9OS/gy0Ewd1gW/PUQIOIQGXBHWNywSc0floSKoMFF8aK2w==",
2102 | "dev": true,
2103 | "requires": {
2104 | "@esbuild/android-arm": "0.17.18",
2105 | "@esbuild/android-arm64": "0.17.18",
2106 | "@esbuild/android-x64": "0.17.18",
2107 | "@esbuild/darwin-arm64": "0.17.18",
2108 | "@esbuild/darwin-x64": "0.17.18",
2109 | "@esbuild/freebsd-arm64": "0.17.18",
2110 | "@esbuild/freebsd-x64": "0.17.18",
2111 | "@esbuild/linux-arm": "0.17.18",
2112 | "@esbuild/linux-arm64": "0.17.18",
2113 | "@esbuild/linux-ia32": "0.17.18",
2114 | "@esbuild/linux-loong64": "0.17.18",
2115 | "@esbuild/linux-mips64el": "0.17.18",
2116 | "@esbuild/linux-ppc64": "0.17.18",
2117 | "@esbuild/linux-riscv64": "0.17.18",
2118 | "@esbuild/linux-s390x": "0.17.18",
2119 | "@esbuild/linux-x64": "0.17.18",
2120 | "@esbuild/netbsd-x64": "0.17.18",
2121 | "@esbuild/openbsd-x64": "0.17.18",
2122 | "@esbuild/sunos-x64": "0.17.18",
2123 | "@esbuild/win32-arm64": "0.17.18",
2124 | "@esbuild/win32-ia32": "0.17.18",
2125 | "@esbuild/win32-x64": "0.17.18"
2126 | }
2127 | },
2128 | "fflate": {
2129 | "version": "0.6.10",
2130 | "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.6.10.tgz",
2131 | "integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg=="
2132 | },
2133 | "fsevents": {
2134 | "version": "2.3.2",
2135 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
2136 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
2137 | "dev": true,
2138 | "optional": true
2139 | },
2140 | "glsl-noise": {
2141 | "version": "0.0.0",
2142 | "resolved": "https://registry.npmjs.org/glsl-noise/-/glsl-noise-0.0.0.tgz",
2143 | "integrity": "sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w=="
2144 | },
2145 | "its-fine": {
2146 | "version": "1.1.1",
2147 | "resolved": "https://registry.npmjs.org/its-fine/-/its-fine-1.1.1.tgz",
2148 | "integrity": "sha512-v1Ia1xl20KbuSGlwoaGsW0oxsw8Be+TrXweidxD9oT/1lAh6O3K3/GIM95Tt6WCiv6W+h2M7RB1TwdoAjQyyKw==",
2149 | "requires": {
2150 | "@types/react-reconciler": "^0.28.0"
2151 | },
2152 | "dependencies": {
2153 | "@types/react-reconciler": {
2154 | "version": "0.28.2",
2155 | "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.2.tgz",
2156 | "integrity": "sha512-8tu6lHzEgYPlfDf/J6GOQdIc+gs+S2yAqlby3zTsB3SP2svlqTYe5fwZNtZyfactP74ShooP2vvi1BOp9ZemWw==",
2157 | "requires": {
2158 | "@types/react": "*"
2159 | }
2160 | }
2161 | }
2162 | },
2163 | "js-tokens": {
2164 | "version": "4.0.0",
2165 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
2166 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
2167 | },
2168 | "ktx-parse": {
2169 | "version": "0.4.5",
2170 | "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-0.4.5.tgz",
2171 | "integrity": "sha512-MK3FOody4TXbFf8Yqv7EBbySw7aPvEcPX++Ipt6Sox+/YMFvR5xaTyhfNSk1AEmMy+RYIw81ctN4IMxCB8OAlg=="
2172 | },
2173 | "lil-gui": {
2174 | "version": "0.17.0",
2175 | "resolved": "https://registry.npmjs.org/lil-gui/-/lil-gui-0.17.0.tgz",
2176 | "integrity": "sha512-MVBHmgY+uEbmJNApAaPbtvNh1RCAeMnKym82SBjtp5rODTYKWtM+MXHCifLe2H2Ti1HuBGBtK/5SyG4ShQ3pUQ==",
2177 | "peer": true
2178 | },
2179 | "lodash": {
2180 | "version": "4.17.21",
2181 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
2182 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
2183 | },
2184 | "lodash.clamp": {
2185 | "version": "4.0.3",
2186 | "resolved": "https://registry.npmjs.org/lodash.clamp/-/lodash.clamp-4.0.3.tgz",
2187 | "integrity": "sha512-HvzRFWjtcguTW7yd8NJBshuNaCa8aqNFtnswdT7f/cMd/1YKy5Zzoq4W/Oxvnx9l7aeY258uSdDfM793+eLsVg=="
2188 | },
2189 | "lodash.omit": {
2190 | "version": "4.5.0",
2191 | "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz",
2192 | "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg=="
2193 | },
2194 | "lodash.pick": {
2195 | "version": "4.4.0",
2196 | "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
2197 | "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q=="
2198 | },
2199 | "loose-envify": {
2200 | "version": "1.4.0",
2201 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
2202 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
2203 | "requires": {
2204 | "js-tokens": "^3.0.0 || ^4.0.0"
2205 | }
2206 | },
2207 | "maath": {
2208 | "version": "0.5.3",
2209 | "resolved": "https://registry.npmjs.org/maath/-/maath-0.5.3.tgz",
2210 | "integrity": "sha512-ut63A4zTd9abtpi+sOHW1fPWPtAFrjK0E17eAthx1k93W/T2cWLKV5oaswyotJVDvvW1EXSdokAqhK5KOu0Qdw==",
2211 | "requires": {}
2212 | },
2213 | "meshline": {
2214 | "version": "3.1.6",
2215 | "resolved": "https://registry.npmjs.org/meshline/-/meshline-3.1.6.tgz",
2216 | "integrity": "sha512-8JZJOdaL5oz3PI/upG8JvP/5FfzYUOhrkJ8np/WKvXzl0/PZ2V9pqTvCIjSKv+w9ccg2xb+yyBhXAwt6ier3ug==",
2217 | "requires": {}
2218 | },
2219 | "mmd-parser": {
2220 | "version": "1.0.4",
2221 | "resolved": "https://registry.npmjs.org/mmd-parser/-/mmd-parser-1.0.4.tgz",
2222 | "integrity": "sha512-Qi0VCU46t2IwfGv5KF0+D/t9cizcDug7qnNoy9Ggk7aucp0tssV8IwTMkBlDbm+VqAf3cdQHTCARKSsuS2MYFg=="
2223 | },
2224 | "nanoid": {
2225 | "version": "3.3.6",
2226 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
2227 | "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
2228 | "dev": true
2229 | },
2230 | "object-assign": {
2231 | "version": "4.1.1",
2232 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
2233 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
2234 | },
2235 | "opentype.js": {
2236 | "version": "1.3.4",
2237 | "resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-1.3.4.tgz",
2238 | "integrity": "sha512-d2JE9RP/6uagpQAVtJoF0pJJA/fgai89Cc50Yp0EJHk+eLp6QQ7gBoblsnubRULNY132I0J1QKMJ+JTbMqz4sw==",
2239 | "requires": {
2240 | "string.prototype.codepointat": "^0.2.1",
2241 | "tiny-inflate": "^1.0.3"
2242 | }
2243 | },
2244 | "picocolors": {
2245 | "version": "1.0.0",
2246 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
2247 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
2248 | "dev": true
2249 | },
2250 | "postcss": {
2251 | "version": "8.4.23",
2252 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz",
2253 | "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==",
2254 | "dev": true,
2255 | "requires": {
2256 | "nanoid": "^3.3.6",
2257 | "picocolors": "^1.0.0",
2258 | "source-map-js": "^1.0.2"
2259 | }
2260 | },
2261 | "potpack": {
2262 | "version": "1.0.2",
2263 | "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz",
2264 | "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ=="
2265 | },
2266 | "prop-types": {
2267 | "version": "15.8.1",
2268 | "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
2269 | "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
2270 | "requires": {
2271 | "loose-envify": "^1.4.0",
2272 | "object-assign": "^4.1.1",
2273 | "react-is": "^16.13.1"
2274 | }
2275 | },
2276 | "react": {
2277 | "version": "18.2.0",
2278 | "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
2279 | "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
2280 | "requires": {
2281 | "loose-envify": "^1.1.0"
2282 | }
2283 | },
2284 | "react-composer": {
2285 | "version": "5.0.3",
2286 | "resolved": "https://registry.npmjs.org/react-composer/-/react-composer-5.0.3.tgz",
2287 | "integrity": "sha512-1uWd07EME6XZvMfapwZmc7NgCZqDemcvicRi3wMJzXsQLvZ3L7fTHVyPy1bZdnWXM4iPjYuNE+uJ41MLKeTtnA==",
2288 | "requires": {
2289 | "prop-types": "^15.6.0"
2290 | }
2291 | },
2292 | "react-dom": {
2293 | "version": "18.2.0",
2294 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
2295 | "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
2296 | "requires": {
2297 | "loose-envify": "^1.1.0",
2298 | "scheduler": "^0.23.0"
2299 | }
2300 | },
2301 | "react-is": {
2302 | "version": "16.13.1",
2303 | "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
2304 | "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
2305 | },
2306 | "react-merge-refs": {
2307 | "version": "1.1.0",
2308 | "resolved": "https://registry.npmjs.org/react-merge-refs/-/react-merge-refs-1.1.0.tgz",
2309 | "integrity": "sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ=="
2310 | },
2311 | "react-reconciler": {
2312 | "version": "0.27.0",
2313 | "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.27.0.tgz",
2314 | "integrity": "sha512-HmMDKciQjYmBRGuuhIaKA1ba/7a+UsM5FzOZsMO2JYHt9Jh8reCb7j1eDC95NOyUlKM9KRyvdx0flBuDvYSBoA==",
2315 | "requires": {
2316 | "loose-envify": "^1.1.0",
2317 | "scheduler": "^0.21.0"
2318 | },
2319 | "dependencies": {
2320 | "scheduler": {
2321 | "version": "0.21.0",
2322 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz",
2323 | "integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==",
2324 | "requires": {
2325 | "loose-envify": "^1.1.0"
2326 | }
2327 | }
2328 | }
2329 | },
2330 | "react-use-measure": {
2331 | "version": "2.1.1",
2332 | "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.1.tgz",
2333 | "integrity": "sha512-nocZhN26cproIiIduswYpV5y5lQpSQS1y/4KuvUCjSKmw7ZWIS/+g3aFnX3WdBkyuGUtTLif3UTqnLLhbDoQig==",
2334 | "requires": {
2335 | "debounce": "^1.2.1"
2336 | }
2337 | },
2338 | "regenerator-runtime": {
2339 | "version": "0.13.11",
2340 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
2341 | "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
2342 | },
2343 | "regexp-to-ast": {
2344 | "version": "0.5.0",
2345 | "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz",
2346 | "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw=="
2347 | },
2348 | "require-from-string": {
2349 | "version": "2.0.2",
2350 | "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
2351 | "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="
2352 | },
2353 | "rollup": {
2354 | "version": "3.21.0",
2355 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.21.0.tgz",
2356 | "integrity": "sha512-ANPhVcyeHvYdQMUyCbczy33nbLzI7RzrBje4uvNiTDJGIMtlKoOStmympwr9OtS1LZxiDmE2wvxHyVhoLtf1KQ==",
2357 | "dev": true,
2358 | "requires": {
2359 | "fsevents": "~2.3.2"
2360 | }
2361 | },
2362 | "scheduler": {
2363 | "version": "0.23.0",
2364 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
2365 | "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
2366 | "requires": {
2367 | "loose-envify": "^1.1.0"
2368 | }
2369 | },
2370 | "source-map-js": {
2371 | "version": "1.0.2",
2372 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
2373 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
2374 | "dev": true
2375 | },
2376 | "stats.js": {
2377 | "version": "0.17.0",
2378 | "resolved": "https://registry.npmjs.org/stats.js/-/stats.js-0.17.0.tgz",
2379 | "integrity": "sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw=="
2380 | },
2381 | "string.prototype.codepointat": {
2382 | "version": "0.2.1",
2383 | "resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz",
2384 | "integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg=="
2385 | },
2386 | "suspend-react": {
2387 | "version": "0.0.8",
2388 | "resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.0.8.tgz",
2389 | "integrity": "sha512-ZC3r8Hu1y0dIThzsGw0RLZplnX9yXwfItcvaIzJc2VQVi8TGyGDlu92syMB5ulybfvGLHAI5Ghzlk23UBPF8xg==",
2390 | "requires": {}
2391 | },
2392 | "three": {
2393 | "version": "0.152.2",
2394 | "resolved": "https://registry.npmjs.org/three/-/three-0.152.2.tgz",
2395 | "integrity": "sha512-Ff9zIpSfkkqcBcpdiFo2f35vA9ZucO+N8TNacJOqaEE6DrB0eufItVMib8bK8Pcju/ZNT6a7blE1GhTpkdsILw=="
2396 | },
2397 | "three-mesh-bvh": {
2398 | "version": "0.5.23",
2399 | "resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.5.23.tgz",
2400 | "integrity": "sha512-nyk+MskdyDgECqkxdv57UjazqqhrMi+Al9PxJN6yFtx1CTW4r0eCQ27FtyYKY5gCIWhxjtNfWYDPVy8lzx6LkA==",
2401 | "requires": {}
2402 | },
2403 | "three-stdlib": {
2404 | "version": "2.21.10",
2405 | "resolved": "https://registry.npmjs.org/three-stdlib/-/three-stdlib-2.21.10.tgz",
2406 | "integrity": "sha512-YGkR3stmgmXSHL27+1A5dWAKKB+e63PLVkRWV/LLZ7f5+ExsqjLPm4YfBmJgkoSLgILYpQs6O7baY9gnIZlmNA==",
2407 | "requires": {
2408 | "@babel/runtime": "^7.16.7",
2409 | "@types/offscreencanvas": "^2019.6.4",
2410 | "@webgpu/glslang": "^0.0.15",
2411 | "chevrotain": "^10.1.2",
2412 | "draco3d": "^1.4.1",
2413 | "fflate": "^0.6.9",
2414 | "ktx-parse": "^0.4.5",
2415 | "mmd-parser": "^1.0.4",
2416 | "opentype.js": "^1.3.3",
2417 | "potpack": "^1.0.1",
2418 | "zstddec": "^0.0.2"
2419 | }
2420 | },
2421 | "tiny-inflate": {
2422 | "version": "1.0.3",
2423 | "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
2424 | "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="
2425 | },
2426 | "troika-three-text": {
2427 | "version": "0.47.1",
2428 | "resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.47.1.tgz",
2429 | "integrity": "sha512-/fPRUmxCkXxyUT8k6REC/aWeFzKbNr37ivrkrplSJNb3JcBUXvVt8MT0Ac5wTUvFsYTviYWprYS4/8Laen08WA==",
2430 | "requires": {
2431 | "bidi-js": "^1.0.2",
2432 | "troika-three-utils": "^0.47.0",
2433 | "troika-worker-utils": "^0.47.0",
2434 | "webgl-sdf-generator": "1.1.1"
2435 | }
2436 | },
2437 | "troika-three-utils": {
2438 | "version": "0.47.0",
2439 | "resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.47.0.tgz",
2440 | "integrity": "sha512-yoVTQxVbpQX3a55giIwqwq6hyJA6oYvq7kaNGwFTeicoWmTZCqqTbytafx1gcuL5umrtw5MYgsxYUSOha+xp5w==",
2441 | "requires": {}
2442 | },
2443 | "troika-worker-utils": {
2444 | "version": "0.47.0",
2445 | "resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.47.0.tgz",
2446 | "integrity": "sha512-PSUc9vunDEkbE23jpgXD3PcF96jQHKjgMjS+4o5g6DEK/ZAPTnldb+FNddhppawfUcuraMFrslo0GmIC8UpEmA=="
2447 | },
2448 | "typescript": {
2449 | "version": "4.9.5",
2450 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
2451 | "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
2452 | "dev": true
2453 | },
2454 | "use-sync-external-store": {
2455 | "version": "1.2.0",
2456 | "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
2457 | "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
2458 | "requires": {}
2459 | },
2460 | "utility-types": {
2461 | "version": "3.10.0",
2462 | "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz",
2463 | "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg=="
2464 | },
2465 | "vite": {
2466 | "version": "4.3.3",
2467 | "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.3.tgz",
2468 | "integrity": "sha512-MwFlLBO4udZXd+VBcezo3u8mC77YQk+ik+fbc0GZWGgzfbPP+8Kf0fldhARqvSYmtIWoAJ5BXPClUbMTlqFxrA==",
2469 | "dev": true,
2470 | "requires": {
2471 | "esbuild": "^0.17.5",
2472 | "fsevents": "~2.3.2",
2473 | "postcss": "^8.4.23",
2474 | "rollup": "^3.21.0"
2475 | }
2476 | },
2477 | "webgl-constants": {
2478 | "version": "1.1.1",
2479 | "resolved": "https://registry.npmjs.org/webgl-constants/-/webgl-constants-1.1.1.tgz",
2480 | "integrity": "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg=="
2481 | },
2482 | "webgl-sdf-generator": {
2483 | "version": "1.1.1",
2484 | "resolved": "https://registry.npmjs.org/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz",
2485 | "integrity": "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA=="
2486 | },
2487 | "zstddec": {
2488 | "version": "0.0.2",
2489 | "resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.0.2.tgz",
2490 | "integrity": "sha512-DCo0oxvcvOTGP/f5FA6tz2Z6wF+FIcEApSTu0zV5sQgn9hoT5lZ9YRAKUraxt9oP7l4e8TnNdi8IZTCX6WCkwA=="
2491 | },
2492 | "zustand": {
2493 | "version": "4.3.7",
2494 | "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.3.7.tgz",
2495 | "integrity": "sha512-dY8ERwB9Nd21ellgkBZFhudER8KVlelZm8388B5nDAXhO/+FZDhYMuRnqDgu5SYyRgz/iaf8RKnbUs/cHfOGlQ==",
2496 | "requires": {
2497 | "use-sync-external-store": "1.2.0"
2498 | }
2499 | }
2500 | }
2501 | }
2502 |
--------------------------------------------------------------------------------