diff --git a/modules.json b/modules.json index b7d28dfdf..4b2afc03d 100644 --- a/modules.json +++ b/modules.json @@ -99,6 +99,11 @@ "physics_2d" ] }, + "ar": { + "tabs": [ + "AugmentedReality" + ] + }, "communication": { "tabs": [] } diff --git a/package.json b/package.json index 1c93d7083..ebe1cac8a 100644 --- a/package.json +++ b/package.json @@ -116,6 +116,7 @@ "react-ace": "^10.1.0", "react-dom": "^18.2.0", "regl": "^2.1.0", + "saar": "^1.0.4", "save-file": "^2.3.1", "source-academy-utils": "^1.0.0", "source-academy-wabt": "^1.0.4", @@ -131,7 +132,6 @@ }, "resolutions": { "@types/react": "^18.2.0", - "esbuild": "^0.18.20", - "**/gl": "^6.0.2" + "esbuild": "^0.18.20" } } diff --git a/src/bundles/ar/AR.ts b/src/bundles/ar/AR.ts new file mode 100644 index 000000000..3f15a5857 --- /dev/null +++ b/src/bundles/ar/AR.ts @@ -0,0 +1,292 @@ +import { ARObject } from 'saar/libraries/object_state_library/ARObject'; +import { OverlayHelper, Toggle } from './OverlayHelper'; +import { Vector3 } from 'saar/libraries/misc'; + +export class ARState { + arObjects: ARObject[] = []; + highlightFrontObject: boolean = false; + selectedObjectId: string | undefined = undefined; + overlay = new OverlayHelper(); + clickCallbacks = new Map void>(); + onClickCallback = (id: string) => { + const callback = this.clickCallbacks.get(id); + callback?.(); + }; +} + +/** + * Initialize AR. + */ +export function initAR() { + (window as any).arController = new ARState(); +} + +/** + * Obtains AR module state. + * + * @returns Current AR state. + */ +export function getModuleState(): ARState { + return (window as any).arController as ARState; +} + +/** + * Calls callback to update AR context in tab. + */ +export function callARCallback() { + const f = (window as any).arControllerCallback as Function; + if (f) { + f(); + } +} + +// Overlay + +/** + * Sets the left toggle. + * + * @param text Label on toggle. + * @param callback Function to call when toggle is clicked. + */ +export function setLeftToggle(text: string, callback: () => void) { + const moduleState = getModuleState(); + if (!moduleState) return; + moduleState.overlay.toggleLeft = new Toggle(text, callback); +} + +/** + * Sets the center toggle. + * + * @param text Label on toggle. + * @param callback Function to call when toggle is clicked. + */ +export function setCenterToggle(text: string, callback: () => void) { + const moduleState = getModuleState(); + if (!moduleState) return; + moduleState.overlay.toggleCenter = new Toggle(text, callback); +} + +/** + * Sets the right toggle. + * + * @param text Label on toggle. + * @param callback Function to call when toggle is clicked. + */ +export function setRightToggle(text: string, callback: () => void) { + const moduleState = getModuleState(); + if (!moduleState) return; + moduleState.overlay.toggleRight = new Toggle(text, callback); +} + +/** + * Resets and hides the left toggle. + */ +export function removeLeftToggle() { + const moduleState = getModuleState(); + if (!moduleState) return; + moduleState.overlay.toggleLeft = undefined; +} + +/** + * Resets and hides the center toggle. + */ +export function removeCenterToggle() { + const moduleState = getModuleState(); + if (!moduleState) return; + moduleState.overlay.toggleCenter = undefined; +} + +/** + * Resets and hides the right toggle. + */ +export function removeRightToggle() { + const moduleState = getModuleState(); + if (!moduleState) return; + moduleState.overlay.toggleRight = undefined; +} + +// Objects + +/** + * Creates an instance of Vector3. + * + * @param x Value of x-axis. + * @param y Value of y-axis. + * @param z Value of z-axis. + * @returns Vector3 created from specified values. + */ +export function createVector3(x: number, y: number, z: number): Vector3 { + return new Vector3(x, y, z); +} + +/** + * Adds the specified object to the augmented world. + * + * @param object ARObject to add. (E.g. cube, sphere, etc..) + */ +export function addARObject(arObject: ARObject) { + const moduleState = getModuleState(); + if (!moduleState) return; + if (moduleState.arObjects.find((item) => item.id === arObject.id)) { + return; // Already in array + } + if (arObject.onSelect) { + moduleState.clickCallbacks.set(arObject.id, () => { + arObject.onSelect?.(arObject); + }); + } + moduleState.arObjects.push(arObject); + callARCallback(); +} + +/** + * Removes the specified object from the augmented world. + * + * @param arObject ARObject to remove. + */ +export function removeARObject(arObject: ARObject) { + const moduleState = getModuleState(); + if (!moduleState) return; + moduleState.arObjects = moduleState.arObjects.filter( + (item) => item.id !== arObject.id, + ); + callARCallback(); +} + +/** + * Obtains the current ARObjects. + */ +export function getARObjects(): ARObject[] { + const moduleState = getModuleState(); + if (!moduleState) return []; + return moduleState.arObjects; +} + +/** + * Sets AR objects from json. + */ +export function setAsARObjects(json: any) { + const moduleState = getModuleState(); + if (!moduleState) return; + if (!(json instanceof Object)) return; + const objects: ARObject[] = []; + for (const item of Object.values(json)) { + const parsedObject = ARObject.fromObject(item); + if (parsedObject) { + objects.push(parsedObject); + } + } + moduleState.arObjects = objects; + callARCallback(); +} + +/** + * Removes all objects in the augmented world. + */ +export function clearARObjects() { + const moduleState = getModuleState(); + if (!moduleState) return; + moduleState.arObjects = []; + callARCallback(); +} + +/** + * Obtains the position of the specified object on the x-axis. + * + * @param arObject AR object to check. + * @returns Value of position on the x-axis. + */ +export function getXPosition(arObject: ARObject): number { + return arObject.position.x; +} +/** + * Obtains the position of the specified object on the y-axis. + * + * @param arObject AR object to check. + * @returns Value of position on the y-axis. + */ +export function getYPosition(arObject: ARObject): number { + return arObject.position.y; +} + +/** + * Obtains the position of the specified object on the z-axis. + * + * @param arObject AR object to check. + * @returns Value of position on the z-axis. + */ +export function getZPosition(arObject: ARObject): number { + return arObject.position.z; +} + +/** + * Moves the specified object to a new position. + * + * @param arObject AR object to move. + * @param position Position to move to. + */ +export function moveARObject(arObject: ARObject, position: Vector3) { + const moduleState = getModuleState(); + if (!moduleState) return; + arObject.position = position; + callARCallback(); +} + +// Highlight + +/** + * Turn on highlighting of object that the user is facing. + * + * @param isEnabled Whether to highlight object in front. + */ +export function setHighlightFrontObject(isEnabled: boolean) { + const moduleState = getModuleState(); + if (!moduleState) return; + moduleState.highlightFrontObject = isEnabled; + callARCallback(); +} + +/** + * Updates the object in line of sight. + * + * @param arObject New object to set. + */ +export function setFrontObject(arObject: ARObject | undefined) { + const moduleState = getModuleState(); + if (!moduleState) return; + moduleState.selectedObjectId = arObject?.id; +} + +/** + * Obtains the first object in the user's line of sight, if any. + * + * @returns ARObject in front of user if found, undefined if not. + */ +export function getFrontObject() { + const moduleState = getModuleState(); + if (!moduleState) return undefined; + return moduleState.arObjects.find( + (arObject) => arObject.id === moduleState.selectedObjectId, + ); +} + +/** + * Sets the select state for the specified AR object. + * + * @param arObject AR object to select/unselect. + * @param isSelected Whether the object is selected. + */ +export function selectObject(arObject: ARObject, isSelected: boolean) { + arObject.isSelected = isSelected; + callARCallback(); +} + +// JSON + +/** + * Obtains the value of a json object at the key. + */ +export function getJsonChild(object: any, key: string) { + if (!(object instanceof Object)) return undefined; + return object[key]; +} diff --git a/src/bundles/ar/ObjectsHelper.ts b/src/bundles/ar/ObjectsHelper.ts new file mode 100644 index 000000000..36466c47d --- /dev/null +++ b/src/bundles/ar/ObjectsHelper.ts @@ -0,0 +1,482 @@ +import { + type ARObject, + CubeObject, + LightObject, + SphereObject, + UIObject, + GltfObject, +} from 'saar/libraries/object_state_library/ARObject'; +import { + AlwaysRender, + FixRotation, + MovementStyle, + OrbitMovement, + type PathItem, + PathMovement, + RenderWithinDistance, + RotateAroundY, + RotateToUser, + SpringMovement, +} from 'saar/libraries/object_state_library/Behaviour'; +import uniqid from 'uniqid'; +import type { UIBasicItem } from 'saar/libraries/object_state_library/ui_component/UIItem'; +import UIRowItem, { + type VerticalAlignment, +} from 'saar/libraries/object_state_library/ui_component/UIRowItem'; +import UIColumnItem, { + type HorizontalAlignment, +} from 'saar/libraries/object_state_library/ui_component/UIColumnItem'; +import UITextItem from 'saar/libraries/object_state_library/ui_component/UITextItem'; +import UIImageItem from 'saar/libraries/object_state_library/ui_component/UIImageItem'; +import { callARCallback } from './AR'; +import UIBase64ImageComponent from 'saar/libraries/object_state_library/ui_component/UIBase64ImageItem'; +import { type Vector3 } from 'saar/libraries/misc'; + +// Objects + +/** + * Creates an instance of an AR cube object. + * + * @param position Position of object in augmented world. + * @param width Width of the cube in metres. + * @param height Height of the cube in metres. + * @param depth Depth of the cube in metres. + * @param color Decimal representation of hex color. + * @param onSelect Function to call when object is tapped. + * @returns Created AR cube object. + */ +export function createCubeObject( + position: Vector3, + width: number, + height: number, + depth: number, + color: number, + onSelect?: () => {}, +): CubeObject { + return new CubeObject( + uniqid(), + position, + width, + height, + depth, + color, + undefined, + undefined, + undefined, + (_: ARObject) => { + onSelect?.(); + }, + ); +} + +/** + * Creates an instance of an AR sphere object. + * + * @param position Position of object in augmented world. + * @param radius Radius of sphere in metres. + * @param color Decimal representation of hex color. + * @param onSelect Function to call when object is tapped. + * @returns Created AR sphere object. + */ +export function createSphereObject( + position: Vector3, + radius: number, + color: number, + onSelect?: () => {}, +): SphereObject { + return new SphereObject( + uniqid(), + position, + radius, + color, + undefined, + undefined, + undefined, + (_: ARObject) => { + onSelect?.(); + }, + ); +} + +/** + * Creates an instance of 3D object with GLTF model. + * Build it with `createInterfaceRow`, `createInterfaceColumn`, `createInterfaceText` and `createInterfaceImage`. + * + * @param position Position of object in augmented world. + * @param src URL of GLTF resources. + * @param scale Scale of loaded object. + * @param onSelect Function to call when object is tapped. + * @returns Created AR interface object. + */ +export function createGltfObject( + position: Vector3, + src: string, + scale: number, + onSelect?: () => {}, +): GltfObject { + return new GltfObject( + uniqid(), + position, + src, + scale, + undefined, + undefined, + undefined, + (_: ARObject) => { + onSelect?.(); + }, + ); +} + +/** + * Creates an instance of AR user interface. + * Build it with `createInterfaceRow`, `createInterfaceColumn`, `createInterfaceText` and `createInterfaceImage`. + * + * @param position Position of object in augmented world. + * @param rootComponent Root UI component containing other components. Can be row, column, image or text. + * @param onSelect Function to call when object is tapped. + * @returns Created AR interface object. + */ +export function createInterfaceObject( + position: Vector3, + rootComponent: UIBasicItem, + onSelect?: () => {}, +): UIObject { + return new UIObject( + uniqid(), + position, + rootComponent.toJSON(), + undefined, + undefined, + undefined, + (_: ARObject) => { + onSelect?.(); + }, + ); +} + +/** + * Creates an instance of light source in augmented world. + * Shines in all direction from a point. + * + * @param position Position of object in augmented world. + * @param intensity Intensity of light. + * @returns Created AR light source object. + */ +export function createLightObject( + position: Vector3, + intensity: number, +): LightObject { + return new LightObject(uniqid(), position, intensity); +} + +// Interface + +/** + * Creates a row component for UI object. + * + * @param children Components within this row. + * @param verticalAlignment Vertical alignment of content. Use `alignmentTop`, `alignmentMiddle` or `alignmentBottom`. + * @param paddingLeft Size of padding on the left. + * @param paddingRight Size of padding on the right. + * @param paddingTop Size of padding on the top. + * @param paddingBottom Size of padding on the bottom. + * @param backgroundColor Background color of row. + * @returns Created row component. + */ +export function createInterfaceRow( + children: UIBasicItem[], + verticalAlignment: VerticalAlignment, + paddingLeft: number, + paddingRight: number, + paddingTop: number, + paddingBottom: number, + backgroundColor: number, +): UIRowItem { + return new UIRowItem({ + children, + verticalAlignment, + padding: { + paddingLeft, + paddingRight, + paddingTop, + paddingBottom, + }, + backgroundColor, + id: uniqid(), + }); +} + +/** + * Creates a row component for UI object. + * + * @param children Components within this column. + * @param horizontalAlignment Horizontal alignment of content. Use `alignmentLeft`, `alignmentCenter` or `alignmentRight`. + * @param paddingLeft Size of padding on the left. + * @param paddingRight Size of padding on the right. + * @param paddingTop Size of padding on the top. + * @param paddingBottom Size of padding on the bottom. + * @param backgroundColor Background color of column. + * @returns Created column component. + */ +export function createInterfaceColumn( + children: UIBasicItem[], + horizontalAlignment: HorizontalAlignment, + paddingLeft: number, + paddingRight: number, + paddingTop: number, + paddingBottom: number, + backgroundColor: number, +): UIColumnItem { + return new UIColumnItem({ + children, + horizontalAlignment, + padding: { + paddingLeft, + paddingRight, + paddingTop, + paddingBottom, + }, + backgroundColor, + id: uniqid(), + }); +} + +/** + * Creates a text component for UI object. + * + * @param text Text to show. + * @param textSize Size of text in metres. + * @param textWidth Width of text. + * @param textAlign Horizontal alignment of text. Use `alignmentLeft`, `alignmentCenter` or `alignmentRight`. + * @param paddingLeft Size of padding on the left. + * @param paddingRight Size of padding on the right. + * @param paddingTop Size of padding on the top. + * @param paddingBottom Size of padding on the bottom. + * @param color Color of text. + * @returns Created text component. + */ +export function createInterfaceText( + text: string, + textSize: number, + textWidth: number, + textAlign: HorizontalAlignment, + paddingLeft: number, + paddingRight: number, + paddingTop: number, + paddingBottom: number, + color: number, +): UITextItem { + return new UITextItem({ + text, + textSize, + textWidth, + textAlign, + padding: { + paddingLeft, + paddingRight, + paddingTop, + paddingBottom, + }, + color, + id: uniqid(), + }); +} + +/** + * Creates an image component for UI object. + * Crops the image to the specified size. + * + * @param src Link to the image. + * @param imageWidth Width of the image. + * @param imageHeight Height of the image. + * @param paddingLeft Size of padding on the left. + * @param paddingRight Size of padding on the right. + * @param paddingTop Size of padding on the top. + * @param paddingBottom Size of padding on the bottom. + * @returns Created interface component. + */ +export function createInterfaceImage( + src: string, + imageWidth: number, + imageHeight: number, + paddingLeft: number, + paddingRight: number, + paddingTop: number, + paddingBottom: number, +): UIImageItem { + return new UIImageItem({ + src, + imageWidth, + imageHeight, + padding: { + paddingLeft, + paddingRight, + paddingTop, + paddingBottom, + }, + id: uniqid(), + }); +} + +/** + * Creates an image component for UI object, using Base64 string. + * Crops the image to the specified size. + * + * @param base64 Base64 string containing the image. + * @param imageWidth Width of the image. + * @param imageHeight Height of the image. + * @param paddingLeft Size of padding on the left. + * @param paddingRight Size of padding on the right. + * @param paddingTop Size of padding on the top. + * @param paddingBottom Size of padding on the bottom. + * @returns Created interface component. + */ +export function createInterfaceBase64Image( + base64: string, + imageWidth: number, + imageHeight: number, + paddingLeft: number, + paddingRight: number, + paddingTop: number, + paddingBottom: number, +): UIBase64ImageComponent { + return new UIBase64ImageComponent({ + base64, + imageWidth, + imageHeight, + padding: { + paddingLeft, + paddingRight, + paddingTop, + paddingBottom, + }, + id: uniqid(), + }); +} + +// Rotation + +/** + * Fix the rotation of the object at the specified angle around the vertical axis. + * + * @param object Object to modifiy. + * @param radians Rotation angle in radians. + */ +export function setFixedRotation(object: ARObject, radians: number) { + object.behaviours.rotation = new FixRotation(radians); + callARCallback(); +} + +/** + * Always rotate the object to where the user is facing. + * + * @param object Object to modifiy. + */ +export function setRotateToUser(object: ARObject) { + object.behaviours.rotation = new RotateToUser(); + callARCallback(); +} + +/** + * Rotates the object continuously around the vertical axis. + * + * @param object Object to modifiy. + */ +export function setRotateAroundY(object: ARObject) { + object.behaviours.rotation = new RotateAroundY(); + callARCallback(); +} + +// Render + +/** + * Always render the object. + * + * @param object Object to modifiy. + */ +export function setAlwaysRender(object: ARObject) { + object.behaviours.render = new AlwaysRender(); + callARCallback(); +} + +/** + * Only render the object when in range. + * + * @param object Object to modifiy. + * @param distance Range of object in metres. + */ +export function setRenderDistance(object: ARObject, distance: number) { + object.behaviours.render = new RenderWithinDistance(distance); + callARCallback(); +} + +// Movement + +/** + * Creates an instance of a path item, to specify movement path. + * + * @param start Start position. + * @param end End position + * @param duration Duration of transition from start to end position. + * @returns Created instance of path item. + */ +export function createPathItem( + start: Vector3, + end: Vector3, + duration: number, +): PathItem { + return { + start, + end, + duration, + style: MovementStyle.Linear, + }; +} + +/** + * Moves object in the specified path, repeat when ended. + * + * @param object Object to modifiy. + * @param path Path to move. Use `createPathItem` to create a path segment. + */ +export function setPathMovement(object: ARObject, path: PathItem[]) { + object.behaviours.movement = new PathMovement(path); + callARCallback(); +} + +/** + * Orbits the object around its current position. + * + * @param object Object to modifiy. + * @param radius Radius of orbit. + * @param duration Duration per round of orbit. + */ +export function setOrbitMovement( + object: ARObject, + radius: number, + duration: number, +) { + object.behaviours.movement = new OrbitMovement(radius, duration); + callARCallback(); +} + +/** + * Animates movement when the position of the object changes. + * + * @param object Object to modifiy. + */ +export function setSpringMovement(object: ARObject) { + object.behaviours.movement = new SpringMovement(); + callARCallback(); +} + +/** + * Removes the movement of the object. + * + * @param object Object to modifiy. + */ +export function clearMovement(object: ARObject) { + object.behaviours.movement = undefined; + callARCallback(); +} diff --git a/src/bundles/ar/OverlayHelper.ts b/src/bundles/ar/OverlayHelper.ts new file mode 100644 index 000000000..8a7d576bb --- /dev/null +++ b/src/bundles/ar/OverlayHelper.ts @@ -0,0 +1,14 @@ +export class Toggle { + text: string; + callback: () => void; + constructor(text: string, callback: () => void) { + this.text = text; + this.callback = callback; + } +} + +export class OverlayHelper { + toggleLeft: Toggle | undefined; + toggleCenter: Toggle | undefined; + toggleRight: Toggle | undefined; +} diff --git a/src/bundles/ar/index.ts b/src/bundles/ar/index.ts new file mode 100644 index 000000000..fab4c2239 --- /dev/null +++ b/src/bundles/ar/index.ts @@ -0,0 +1,72 @@ +/** + * Module for creating and interacting with augmented reality content. + * + * Allows starting of an XR context within Source Academy. + * Currently only works on Android. + * Desktop version of Chrome can be used to run an emulator via WebXR API Emulator plugin. + * @module ar + * @author Chong Wen Hao + */ + +import { HorizontalAlignment } from 'saar/libraries/object_state_library/ui_component/UIColumnItem'; +import { VerticalAlignment } from 'saar/libraries/object_state_library/ui_component/UIRowItem'; + +/* + To access things like the context or module state you can just import the context + using the import below + */ + +export { + initAR, + setLeftToggle, + setCenterToggle, + setRightToggle, + removeLeftToggle, + removeCenterToggle, + removeRightToggle, + createVector3, + addARObject, + removeARObject, + getARObjects, + setAsARObjects, + clearARObjects, + getXPosition, + getYPosition, + getZPosition, + moveARObject, + setHighlightFrontObject, + selectObject, + getFrontObject, + getJsonChild, +} from './AR'; + +export { + createCubeObject, + createSphereObject, + createGltfObject, + createInterfaceObject, + createLightObject, + createInterfaceRow, + createInterfaceColumn, + createInterfaceText, + createInterfaceImage, + createInterfaceBase64Image, + setFixedRotation, + setRotateToUser, + setRotateAroundY, + setAlwaysRender, + setRenderDistance, + createPathItem, + setPathMovement, + setOrbitMovement, + setSpringMovement, + clearMovement, +} from './ObjectsHelper'; + +export const alignmentTop = VerticalAlignment.Top; +export const alignmentMiddle = VerticalAlignment.Middle; +export const alignmentBottom = VerticalAlignment.Bottom; + +export const alignmentLeft = HorizontalAlignment.Left; +export const alignmentCenter = HorizontalAlignment.Center; +export const alignmentRight = HorizontalAlignment.Right; diff --git a/src/tabs/AugmentedReality/AugmentedContent.tsx b/src/tabs/AugmentedReality/AugmentedContent.tsx new file mode 100644 index 000000000..4fb36ad21 --- /dev/null +++ b/src/tabs/AugmentedReality/AugmentedContent.tsx @@ -0,0 +1,189 @@ +import { useState, type RefObject, useRef, useEffect } from 'react'; +import { + getModuleState, + type ARState, + setFrontObject, +} from '../../bundles/ar/AR'; +import type { OverlayHelper } from '../../bundles/ar/OverlayHelper'; +import { useScreenState } from 'saar/libraries/screen_state_library/ScreenStateContext'; +import { usePlayArea } from 'saar/libraries/calibration_library/PlayAreaContext'; +import { useControls } from 'saar/libraries/controls_library/ControlsContext'; +import { ARObject } from 'saar/libraries/object_state_library/ARObject'; + +/** + * Content to be shown on screen. + */ +export function AugmentedContent(props: ARState) { + const screenState = useScreenState(); + const playArea = usePlayArea(); + const controls = useControls(); + const [objects, setObjects] = useState([]); + const highlightFrontObject = useRef(props.highlightFrontObject); + const objectsRef = useRef(); + const [timeOffset, setTimeOffset] = useState(0); + + useEffect(() => { + controls.setObjectInSightCallback((prev, current) => { + if (!highlightFrontObject.current) { + return; + } + if (prev) { + const object = objectsRef.current?.find( + (item) => item.uuid === prev.uuid, + ); + if (object) { + object.isInFront = false; + } + } + if (current) { + const object = objectsRef.current?.find( + (item) => item.uuid === current.uuid, + ); + if (object) { + object.isInFront = true; + setFrontObject(object); + } else { + setFrontObject(undefined); + } + } else { + setFrontObject(undefined); + } + }); + (window as any).arControllerCallback = () => { + const newState = getModuleState(); + updateObjects(newState); + highlightFrontObject.current = newState.highlightFrontObject; + }; + updateObjects(props); + }, []); + + useEffect(() => { + setupToggles(props.overlay, screenState.overlayRef, () => { + playArea.setCameraAsOrigin(); + }); + }, [ + props.overlay.toggleCenter, + props.overlay.toggleLeft, + props.overlay.toggleRight, + ]); + + function updateObjects(state: ARState) { + const newObjects: ARObject[] = []; + state.arObjects.forEach((object) => { + const newObject = ARObject.fromObject(object, getCurrentTime); + if (newObject) { + newObjects.push(newObject); + } + }); + newObjects.forEach((object) => { + object.onSelect = () => { + const moduleState = getModuleState(); + if (moduleState) { + const callback = moduleState.clickCallbacks.get(object.id); + callback?.(); + } + }; + }); + setObjects(newObjects); + objectsRef.current = newObjects; + } + + // Toggles + + function setupToggles( + overlayHelper: OverlayHelper, + overlayRef: RefObject | null, + recalibrate: () => void, + ) { + if (!overlayRef || !overlayRef.current) return; + const overlay = overlayRef.current; + // Recalibrate + const recalibrateToggle = overlay?.querySelector( + '#recalibrate-toggle', + ) as HTMLElement; + if (recalibrateToggle) { + recalibrateToggle.onclick = recalibrate; + } + // Left + const leftToggle = overlay?.querySelector('#left-toggle') as HTMLElement; + if (leftToggle) { + if (overlayHelper.toggleLeft) { + leftToggle.style.display = 'block'; + leftToggle.textContent = overlayHelper.toggleLeft.text; + leftToggle.onclick = () => { + overlayHelper.toggleLeft?.callback(); + }; + } else { + leftToggle.style.display = 'none'; + leftToggle.textContent = ''; + leftToggle.onclick = () => {}; + } + } + // Center + const centerToggle = overlay?.querySelector( + '#center-toggle', + ) as HTMLElement; + if (centerToggle) { + if (overlayHelper.toggleCenter) { + centerToggle.style.display = 'block'; + centerToggle.textContent = overlayHelper.toggleCenter.text; + centerToggle.onclick = () => { + overlayHelper.toggleCenter?.callback(); + }; + } else { + centerToggle.style.display = 'none'; + centerToggle.textContent = ''; + centerToggle.onclick = () => {}; + } + } + // Right + const rightToggle = overlay?.querySelector('#right-toggle') as HTMLElement; + if (rightToggle) { + if (overlayHelper.toggleRight) { + rightToggle.style.display = 'block'; + rightToggle.textContent = overlayHelper.toggleRight.text; + rightToggle.onclick = () => { + overlayHelper.toggleRight?.callback(); + }; + } else { + rightToggle.style.display = 'none'; + rightToggle.textContent = ''; + rightToggle.onclick = () => {}; + } + } + } + + // Time + + useEffect(() => { + try { + fetch('https://worldtimeapi.org/api/timezone/Asia/Singapore') + .then((response) => response.json()) + .then((data) => { + let time = new Date(data.datetime).getTime(); + let offset = time - new Date().getTime(); + setTimeOffset(offset); + console.log('Time offset', offset); + }) + .catch((error) => { + console.log(error); + }); + } catch {} + }, []); + + /** + * Obtains the current time that is synced with other devices. + * Offset obtained via this free api: https://worldtimeapi.org/api/timezone/Asia/Singapore + * + * @returns Corrected current timing. + */ + function getCurrentTime() { + return new Date().getTime() + timeOffset; + } + + return ( + + {objects.map((item) => item.getComponent(playArea.getCameraPosition))} + + ); +} diff --git a/src/tabs/AugmentedReality/AugmentedLayer.tsx b/src/tabs/AugmentedReality/AugmentedLayer.tsx new file mode 100644 index 000000000..144de2fc2 --- /dev/null +++ b/src/tabs/AugmentedReality/AugmentedLayer.tsx @@ -0,0 +1,18 @@ +import type { ARState } from '../../bundles/ar/AR'; +import { PlayAreaContext } from 'saar/libraries/calibration_library/PlayAreaContext'; +import { ControlsContext } from 'saar/libraries/controls_library/ControlsContext'; +import { AugmentedContent } from './AugmentedContent'; + +/** + * Component for AR layer. + * Wraps AugmentedContext with required contexts. + */ +export function AugmentedLayer(props: ARState) { + return ( + + + + + + ); +} diff --git a/src/tabs/AugmentedReality/Overlay.tsx b/src/tabs/AugmentedReality/Overlay.tsx new file mode 100644 index 000000000..f6cfa2747 --- /dev/null +++ b/src/tabs/AugmentedReality/Overlay.tsx @@ -0,0 +1,97 @@ +import { ARButton } from 'saar/libraries/misc'; + +/** + * Default overlay for AR in Source Academy. + * Toggles are hidden until defined. + */ +export function Overlay() { + return ( +
+
+ +
+
+ + Exit + +
+
+ ); +} diff --git a/src/tabs/AugmentedReality/StartButton.tsx b/src/tabs/AugmentedReality/StartButton.tsx new file mode 100644 index 000000000..78d6ce2d8 --- /dev/null +++ b/src/tabs/AugmentedReality/StartButton.tsx @@ -0,0 +1,32 @@ +import { useEffect } from 'react'; +import type { ARState } from '../../bundles/ar/AR'; +import { useScreenState } from 'saar/libraries/screen_state_library/ScreenStateContext'; +import { ARButton } from 'saar/libraries/misc'; +import { Overlay } from './Overlay'; +import { AugmentedLayer } from './AugmentedLayer'; + +/** + * Toggle to start AR context, for tab. + */ +export function StartButton(props: ARState) { + const screenState = useScreenState(); + + useEffect(() => { + screenState.setState(, ); + }, []); + + return ( +
+ + {screenState.component} +
+ ); +} diff --git a/src/tabs/AugmentedReality/index.tsx b/src/tabs/AugmentedReality/index.tsx new file mode 100644 index 000000000..fbfe3ab89 --- /dev/null +++ b/src/tabs/AugmentedReality/index.tsx @@ -0,0 +1,61 @@ +import React from 'react'; +import { getModuleState } from '../../bundles/ar/AR'; +import { ScreenStateContext } from 'saar/libraries/screen_state_library/ScreenStateContext'; +import { StartButton } from './StartButton'; + +/** + * Tab for viewing augmented reality content + * @module ar + * @author Chong Wen Hao + */ + +/** + * React Component props for the Tab. + */ +type Props = {}; + +/** + * The main React Component of the Tab. + */ +class ARMainComponent extends React.Component { + public render() { + return ( +
+

Instructions:

+

Click on the toggle below to enter AR mode.

+ + + +
+ ); + } +} + +export default { + /** + * This function will be called to determine if the component will be + * rendered. Currently spawns when the result in the REPL is "test". + * @param {DebuggerContext} context + * @returns {boolean} + */ + toSpawn: (_: any) => getModuleState() !== undefined, + + /** + * This function will be called to render the module tab in the side contents + * on Source Academy frontend. + * @param {DebuggerContext} context + */ + body: (_context: any) => , + + /** + * The Tab's icon tooltip in the side contents on Source Academy frontend. + */ + label: 'AR Tab', + + /** + * BlueprintJS IconName element's name, used to render the icon which will be + * displayed in the side contents panel. + * @see https://blueprintjs.com/docs/#icons + */ + iconName: 'intelligence', +}; diff --git a/src/tsconfig.json b/src/tsconfig.json index f9bb8b7ee..15f221283 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -38,7 +38,7 @@ // ], "paths": { "js-slang/context": ["./typings/@types/js-slang/context.d.ts"] - }, + } }, /* Specifies an array of filenames or patterns to include in the program. These filenames are resolved relative to the directory containing the tsconfig.json file. */ "include": ["."], diff --git a/yarn.lock b/yarn.lock index c37663571..069bf060a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -278,6 +278,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/runtime@^7.11.2", "@babel/runtime@^7.17.8": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.0.tgz#584c450063ffda59697021430cb47101b085951e" + integrity sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/runtime@^7.20.7", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7": version "7.20.13" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.13.tgz#7055ab8a7cff2b8f6058bf6ae45ff84ad2aded4b" @@ -813,6 +820,11 @@ "@jscad/array-utils" "2.1.4" "@jscad/modeling" "2.9.6" +"@mediapipe/tasks-vision@0.10.8": + version "0.10.8" + resolved "https://registry.yarnpkg.com/@mediapipe/tasks-vision/-/tasks-vision-0.10.8.tgz#a78e137018a19933b7a1d0e887d553d4ab833d10" + integrity sha512-Rp7ll8BHrKB3wXaRFKhrltwZl1CiXGdibPxuWXvqGnKTnv8fqa/nvftYNuSbf+pbJWKYCXdBtYTITdAUTGGh0Q== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -855,6 +867,105 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== +"@react-spring/animated@~9.6.1": + version "9.6.1" + resolved "https://registry.yarnpkg.com/@react-spring/animated/-/animated-9.6.1.tgz#ccc626d847cbe346f5f8815d0928183c647eb425" + integrity sha512-ls/rJBrAqiAYozjLo5EPPLLOb1LM0lNVQcXODTC1SMtS6DbuBCPaKco5svFUQFMP2dso3O+qcC4k9FsKc0KxMQ== + dependencies: + "@react-spring/shared" "~9.6.1" + "@react-spring/types" "~9.6.1" + +"@react-spring/core@~9.6.1": + version "9.6.1" + resolved "https://registry.yarnpkg.com/@react-spring/core/-/core-9.6.1.tgz#ebe07c20682b360b06af116ea24e2b609e778c10" + integrity sha512-3HAAinAyCPessyQNNXe5W0OHzRfa8Yo5P748paPcmMowZ/4sMfaZ2ZB6e5x5khQI8NusOHj8nquoutd6FRY5WQ== + dependencies: + "@react-spring/animated" "~9.6.1" + "@react-spring/rafz" "~9.6.1" + "@react-spring/shared" "~9.6.1" + "@react-spring/types" "~9.6.1" + +"@react-spring/rafz@~9.6.1": + version "9.6.1" + resolved "https://registry.yarnpkg.com/@react-spring/rafz/-/rafz-9.6.1.tgz#d71aafb92b78b24e4ff84639f52745afc285c38d" + integrity sha512-v6qbgNRpztJFFfSE3e2W1Uz+g8KnIBs6SmzCzcVVF61GdGfGOuBrbjIcp+nUz301awVmREKi4eMQb2Ab2gGgyQ== + +"@react-spring/shared@~9.6.1": + version "9.6.1" + resolved "https://registry.yarnpkg.com/@react-spring/shared/-/shared-9.6.1.tgz#4e2e4296910656c02bd9fd54c559702bc836ac4e" + integrity sha512-PBFBXabxFEuF8enNLkVqMC9h5uLRBo6GQhRMQT/nRTnemVENimgRd+0ZT4yFnAQ0AxWNiJfX3qux+bW2LbG6Bw== + dependencies: + "@react-spring/rafz" "~9.6.1" + "@react-spring/types" "~9.6.1" + +"@react-spring/three@9.6.1", "@react-spring/three@~9.6.1": + version "9.6.1" + resolved "https://registry.yarnpkg.com/@react-spring/three/-/three-9.6.1.tgz#095fcd1dc6509127c33c14486d88289b89baeb9d" + integrity sha512-Tyw2YhZPKJAX3t2FcqvpLRb71CyTe1GvT3V+i+xJzfALgpk10uPGdGaQQ5Xrzmok1340DAeg2pR/MCfaW7b8AA== + dependencies: + "@react-spring/animated" "~9.6.1" + "@react-spring/core" "~9.6.1" + "@react-spring/shared" "~9.6.1" + "@react-spring/types" "~9.6.1" + +"@react-spring/types@~9.6.1": + version "9.6.1" + resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.6.1.tgz#913d3a68c5cbc1124fdb18eff919432f7b6abdde" + integrity sha512-POu8Mk0hIU3lRXB3bGIGe4VHIwwDsQyoD1F394OK7STTiX9w4dG3cTLljjYswkQN+hDSHRrj4O36kuVa7KPU8Q== + +"@react-three/drei@^9.97.0": + version "9.99.5" + resolved "https://registry.yarnpkg.com/@react-three/drei/-/drei-9.99.5.tgz#20021a2f8db8bfd2dc1508d057c5e6d0dee919c8" + integrity sha512-9gqzYvuyd1Wtvv/+s+FdSogQdehDXUvZmhqKXw8EeBbqZaySpjvMcTf1B2hHNVVkgKl/zQHRf2z+240UsrrPXg== + dependencies: + "@babel/runtime" "^7.11.2" + "@mediapipe/tasks-vision" "0.10.8" + "@react-spring/three" "~9.6.1" + "@use-gesture/react" "^10.2.24" + camera-controls "^2.4.2" + cross-env "^7.0.3" + detect-gpu "^5.0.28" + glsl-noise "^0.0.0" + maath "^0.10.7" + meshline "^3.1.6" + react-composer "^5.0.3" + stats-gl "^2.0.0" + stats.js "^0.17.0" + suspend-react "^0.1.3" + three-mesh-bvh "^0.7.0" + three-stdlib "^2.29.4" + troika-three-text "^0.49.0" + tunnel-rat "^0.1.2" + utility-types "^3.10.0" + uuid "^9.0.1" + zustand "^3.7.1" + +"@react-three/fiber@^8.15.16": + version "8.15.16" + resolved "https://registry.yarnpkg.com/@react-three/fiber/-/fiber-8.15.16.tgz#eadaa9432dd55a7d7334c42880470c7ac3c30e83" + integrity sha512-4f47K9e2mP8W/guNtu3e2J/Nt6GwKTsX/YP2dktPZRcpHYEsqfXCO8kSfvVMb+lQ8wR0HoFzggqdnGuhZaui0g== + dependencies: + "@babel/runtime" "^7.17.8" + "@types/react-reconciler" "^0.26.7" + "@types/webxr" "*" + base64-js "^1.5.1" + buffer "^6.0.3" + its-fine "^1.0.6" + react-reconciler "^0.27.0" + react-use-measure "^2.1.1" + scheduler "^0.21.0" + suspend-react "^0.1.3" + zustand "^3.7.1" + +"@react-three/xr@^5.7.1": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@react-three/xr/-/xr-5.7.1.tgz#ebba10f08047691f1ee18aa4bfbc1106cfebf811" + integrity sha512-GaRUSA+lE8VJF/NrXq7QQByZ4UGHbQQ4rs3QCphZs9fVidK86hGrMOQ0kL79gZc5pa3V5uFGlOhNcUdsTYE3Bg== + dependencies: + "@types/webxr" "*" + three-stdlib "^2.21.1" + zustand "^3.7.1" + "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" @@ -983,6 +1094,11 @@ resolved "https://registry.yarnpkg.com/@types/dom-mediacapture-record/-/dom-mediacapture-record-1.0.14.tgz#5224cdfbe43a77a514192762209c40c8c73563ba" integrity sha512-dNVhzNPFPUwm/nABmEZRTXGWiU7EFApgbYB0WTlZlsC1iXOMyG/5TrElNMToyQ3jbtiZFaZeIjEN/lpo4oMQWw== +"@types/draco3d@^1.4.0": + version "1.4.9" + resolved "https://registry.yarnpkg.com/@types/draco3d/-/draco3d-1.4.9.tgz#eb3eb7c5fd6f3490ab86ed7ebf36e595a9dc179b" + integrity sha512-4MMUjMQb4yA5fJ4osXx+QxGHt0/ZSy4spT6jL1HM7Tn8OJEC35siqdnpOo+HxPhYjqEFumKfGVF9hJfdyKBIBA== + "@types/eslint@^8.56.2": version "8.56.6" resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.6.tgz#d5dc16cac025d313ee101108ba5714ea10eb3ed0" @@ -1066,6 +1182,11 @@ dependencies: undici-types "~5.26.4" +"@types/offscreencanvas@^2019.6.4": + version "2019.7.3" + resolved "https://registry.yarnpkg.com/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz#90267db13f64d6e9ccb5ae3eac92786a7c77a516" + integrity sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A== + "@types/plotly.js-dist@npm:@types/plotly.js": version "2.12.16" resolved "https://registry.yarnpkg.com/@types/plotly.js/-/plotly.js-2.12.16.tgz#99a7ef35052ba5ef985a036ba0ec2d549a604a3c" @@ -1083,6 +1204,20 @@ dependencies: "@types/react" "*" +"@types/react-reconciler@^0.26.7": + version "0.26.7" + resolved "https://registry.yarnpkg.com/@types/react-reconciler/-/react-reconciler-0.26.7.tgz#0c4643f30821ae057e401b0d9037e03e8e9b2a36" + integrity sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ== + dependencies: + "@types/react" "*" + +"@types/react-reconciler@^0.28.0": + version "0.28.8" + resolved "https://registry.yarnpkg.com/@types/react-reconciler/-/react-reconciler-0.28.8.tgz#e51710572bcccf214306833c2438575d310b3e98" + integrity sha512-SN9c4kxXZonFhbX4hJrZy37yw9e7EIxcpHCxQv5JUS18wDE5ovkQKlqQEkufdJCCMfuI9BnjUJvhYeJ9x5Ra7g== + dependencies: + "@types/react" "*" + "@types/react@*", "@types/react@^18.2.0": version "18.2.33" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.33.tgz#055356243dc4350a9ee6c6a2c07c5cae12e38877" @@ -1107,11 +1242,31 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== +"@types/stats.js@*": + version "0.17.3" + resolved "https://registry.yarnpkg.com/@types/stats.js/-/stats.js-0.17.3.tgz#705446e12ce0fad618557dd88236f51148b7a935" + integrity sha512-pXNfAD3KHOdif9EQXZ9deK82HVNaXP5ZIF5RP2QG6OQFNTaY2YIetfrE9t528vEreGQvEPRDDc8muaoYeK0SxQ== + +"@types/three@^0.161.2": + version "0.161.2" + resolved "https://registry.yarnpkg.com/@types/three/-/three-0.161.2.tgz#3c7e3f40869ad52970f517583cc200472e8918bf" + integrity sha512-DazpZ+cIfBzbW/p0zm6G8CS03HBMd748A3R1ZOXHpqaXZLv2I5zNgQUrRG//UfJ6zYFp2cUoCQaOLaz8ubH07w== + dependencies: + "@types/stats.js" "*" + "@types/webxr" "*" + fflate "~0.6.10" + meshoptimizer "~0.18.1" + "@types/tough-cookie@*": version "4.0.2" resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397" integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw== +"@types/webxr@*", "@types/webxr@^0.5.2": + version "0.5.14" + resolved "https://registry.yarnpkg.com/@types/webxr/-/webxr-0.5.14.tgz#9a03121a4912ea113b31e5c9c17f164d4fff8a1f" + integrity sha512-UEMMm/Xn3DtEa+gpzUrOcDj+SJS1tk5YodjwOxcqStNhCfPcwgyC5Srg2ToVKyg2Fhq16Ffpb0UWUQHqoT9AMA== + "@types/yargs-parser@*": version "21.0.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" @@ -1311,6 +1466,18 @@ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== +"@use-gesture/core@10.3.0": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@use-gesture/core/-/core-10.3.0.tgz#9afd3777a45b2a08990a5dcfcf8d9ddd55b00db9" + integrity sha512-rh+6MND31zfHcy9VU3dOZCqGY511lvGcfyJenN4cWZe0u1BH6brBpBddLVXhF2r4BMqWbvxfsbL7D287thJU2A== + +"@use-gesture/react@^10.2.24": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@use-gesture/react/-/react-10.3.0.tgz#180534c821fd635c2853cbcfa813f92c94f27e3f" + integrity sha512-3zc+Ve99z4usVP6l9knYVbVnZgfqhKah7sIG+PS2w+vpig2v2OLct05vs+ZXMzwxdNCMka8B+8WlOo0z6Pn6DA== + dependencies: + "@use-gesture/core" "10.3.0" + "@vitejs/plugin-react@^4.0.4": version "4.0.4" resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-4.0.4.tgz#31c3f779dc534e045c4b134e7cf7b150af0a7646" @@ -1694,7 +1861,7 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.3.1: +base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -1704,6 +1871,13 @@ basic-auth@^1.0.3: resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-1.1.0.tgz#45221ee429f7ee1e5035be3f51533f1cdfd29884" integrity sha512-CtGuTyWf3ig+sgRyC7uP6DM3N+5ur/p8L+FPfsd+BbIfIs74TFfCajZTHnCw6K5dqM0bZEbRIqRy1fAdiUJhTA== +bidi-js@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/bidi-js/-/bidi-js-1.0.3.tgz#6f8bcf3c877c4d9220ddf49b9bb6930c88f877d2" + integrity sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw== + dependencies: + require-from-string "^2.0.2" + binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" @@ -1789,6 +1963,14 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + cacache@^16.1.0: version "16.1.3" resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" @@ -1847,6 +2029,11 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== +camera-controls@^2.4.2: + version "2.8.3" + resolved "https://registry.yarnpkg.com/camera-controls/-/camera-controls-2.8.3.tgz#54c296d21564dede84e56ffd6b75b3daa3c76e90" + integrity sha512-zFjqUR6onLkG+z1A6vAWfzovxZxWVSvp6e5t3lfZgfgPZtX3n74aykNAUaoRbq8Y3tOxadHkDjbfGDOP9hFf2w== + camera-unproject@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/camera-unproject/-/camera-unproject-1.0.1.tgz#86927a9d6d5340a8c9e36da840f7ccb6d6da12cf" @@ -2108,6 +2295,13 @@ create-jest@^29.7.0: jest-util "^29.7.0" prompts "^2.0.1" +cross-env@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" + integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw== + dependencies: + cross-spawn "^7.0.1" + cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -2119,7 +2313,7 @@ cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -2201,6 +2395,11 @@ dayjs@^1.10.4: resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2" integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ== +debounce@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" + integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== + debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" @@ -2303,6 +2502,13 @@ depd@^1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== +detect-gpu@^5.0.28: + version "5.0.38" + resolved "https://registry.yarnpkg.com/detect-gpu/-/detect-gpu-5.0.38.tgz#1c05ce728ea1229d16db15b865631609bf0d6952" + integrity sha512-36QeGHSXYcJ/RfrnPEScR8GDprbXFG4ZhXsfVNVHztZr38+fRxgHnJl3CjYXXjbeRUhu3ZZBJh6Lg0A9v0Qd8A== + dependencies: + webgl-constants "^1.1.1" + detect-libc@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd" @@ -2372,6 +2578,11 @@ dot-case@^3.0.4: no-case "^3.0.4" tslib "^2.0.3" +draco3d@^1.4.1: + version "1.5.7" + resolved "https://registry.yarnpkg.com/draco3d/-/draco3d-1.5.7.tgz#94f9bce293eb8920c159dc91a4ce9124a9e899e0" + integrity sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ== + dtype@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/dtype/-/dtype-2.0.0.tgz#cd052323ce061444ecd2e8f5748f69a29be28434" @@ -2939,6 +3150,11 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +fflate@^0.6.9, fflate@~0.6.10: + version "0.6.10" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.6.10.tgz#5f40f9659205936a2d18abf88b2e7781662b6d43" + integrity sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg== + file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" @@ -3256,6 +3472,11 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +glsl-noise@^0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/glsl-noise/-/glsl-noise-0.0.0.tgz#367745f3a33382c0eeec4cb54b7e99cfc1d7670b" + integrity sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w== + glsl-tokenizer@^2.1.5: version "2.1.5" resolved "https://registry.yarnpkg.com/glsl-tokenizer/-/glsl-tokenizer-2.1.5.tgz#1c2e78c16589933c274ba278d0a63b370c5fee1a" @@ -3468,7 +3689,7 @@ iconv-lite@0.6.3, iconv-lite@^0.6.2: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -ieee754@^1.1.13: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -3852,6 +4073,13 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +its-fine@^1.0.6: + version "1.1.1" + resolved "https://registry.yarnpkg.com/its-fine/-/its-fine-1.1.1.tgz#e74b93fddd487441f978a50f64f0f5af4d2fc38e" + integrity sha512-v1Ia1xl20KbuSGlwoaGsW0oxsw8Be+TrXweidxD9oT/1lAh6O3K3/GIM95Tt6WCiv6W+h2M7RB1TwdoAjQyyKw== + dependencies: + "@types/react-reconciler" "^0.28.0" + jest-changed-files@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" @@ -4518,6 +4746,11 @@ lunr@^2.3.9: resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== +maath@^0.10.7: + version "0.10.7" + resolved "https://registry.yarnpkg.com/maath/-/maath-0.10.7.tgz#9289b42a5db8ac5b26407b3bfca4e3bebefe50b4" + integrity sha512-zQ2xd7dNOIVTjAS+hj22fyj1EFYmOJX6tzKjZ92r6WDoq8hyFxjuGA2q950tmR4iC/EKXoMQdSipkaJVuUHDTg== + make-dir@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" @@ -4581,6 +4814,16 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== +meshline@^3.1.6: + version "3.2.0" + resolved "https://registry.yarnpkg.com/meshline/-/meshline-3.2.0.tgz#0fcffd1fcae780b0e6bf0db991c8d7384154b075" + integrity sha512-ZaJkC967GTuef7UBdO0rGPX544oIWaNo7tYedVHSoR2lje6RR16fX8IsgMxgxoYYERtjqsRWIYBSPBxG4QR84Q== + +meshoptimizer@~0.18.1: + version "0.18.1" + resolved "https://registry.yarnpkg.com/meshoptimizer/-/meshoptimizer-0.18.1.tgz#cdb90907f30a7b5b1190facd3b7ee6b7087797d8" + integrity sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw== + micromatch@^4.0.2, micromatch@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" @@ -5235,6 +5478,11 @@ postinstall-postinstall@^2.1.0: resolved "https://registry.yarnpkg.com/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz#4f7f77441ef539d1512c40bd04c71b06a4704ca3" integrity sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ== +potpack@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/potpack/-/potpack-1.0.2.tgz#23b99e64eb74f5741ffe7656b5b5c4ddce8dfc14" + integrity sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ== + prebuild-install@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45" @@ -5312,7 +5560,7 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -5392,6 +5640,13 @@ react-ace@^10.1.0: lodash.isequal "^4.5.0" prop-types "^15.7.2" +react-composer@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/react-composer/-/react-composer-5.0.3.tgz#7beb9513da5e8687f4f434ea1333ef36a4f3091b" + integrity sha512-1uWd07EME6XZvMfapwZmc7NgCZqDemcvicRi3wMJzXsQLvZ3L7fTHVyPy1bZdnWXM4iPjYuNE+uJ41MLKeTtnA== + dependencies: + prop-types "^15.6.0" + react-dom@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" @@ -5435,6 +5690,14 @@ react-popper@^2.3.0: react-fast-compare "^3.0.1" warning "^4.0.2" +react-reconciler@^0.27.0: + version "0.27.0" + resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.27.0.tgz#360124fdf2d76447c7491ee5f0e04503ed9acf5b" + integrity sha512-HmMDKciQjYmBRGuuhIaKA1ba/7a+UsM5FzOZsMO2JYHt9Jh8reCb7j1eDC95NOyUlKM9KRyvdx0flBuDvYSBoA== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.21.0" + react-refresh@^0.14.0: version "0.14.0" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e" @@ -5467,6 +5730,13 @@ react-uid@^2.3.3: dependencies: tslib "^2.0.0" +react-use-measure@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/react-use-measure/-/react-use-measure-2.1.1.tgz#5824537f4ee01c9469c45d5f7a8446177c6cc4ba" + integrity sha512-nocZhN26cproIiIduswYpV5y5lQpSQS1y/4KuvUCjSKmw7ZWIS/+g3aFnX3WdBkyuGUtTLif3UTqnLLhbDoQig== + dependencies: + debounce "^1.2.1" + react@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" @@ -5510,6 +5780,11 @@ regenerator-runtime@^0.13.11: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + regexp.prototype.flags@^1.4.3, regexp.prototype.flags@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" @@ -5535,6 +5810,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" @@ -5614,6 +5894,21 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +saar@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/saar/-/saar-1.0.4.tgz#e22ccb688303fddc40341bc436e6c8c162967e43" + integrity sha512-wsbTNV1EnMvbA9xtcr2JG91Dy3eykWC6Dacs1XoTwjWyjbab4npr9e2UVq3o58elryo45z7AOuOpydRXmlvu4A== + dependencies: + "@react-spring/three" "9.6.1" + "@react-three/drei" "^9.97.0" + "@react-three/fiber" "^8.15.16" + "@react-three/xr" "^5.7.1" + "@types/three" "^0.161.2" + os "^0.1.2" + spring "^0.0.0" + three "^0.161.0" + uniqid "^5.4.0" + safe-array-concat@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" @@ -5671,6 +5966,13 @@ saxes@^6.0.0: dependencies: xmlchars "^2.2.0" +scheduler@^0.21.0: + version "0.21.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.21.0.tgz#6fd2532ff5a6d877b6edb12f00d8ab7e8f308820" + integrity sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ== + dependencies: + loose-envify "^1.1.0" + scheduler@^0.23.0: version "0.23.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" @@ -5903,6 +6205,11 @@ split2@^3.1.0: dependencies: readable-stream "^3.0.0" +spring@^0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/spring/-/spring-0.0.0.tgz#27a9f571d49f3f29e90c6b9625364073a8353815" + integrity sha512-hQKa8vrkjMLCR5HUMQNRS5oUA0FKSgSvpLSyu6dcNUBc1uCfZ+TssIQXcKQtV4JP8QYGwKtz+6d630ovL4lzeg== + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -5922,6 +6229,16 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" +stats-gl@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/stats-gl/-/stats-gl-2.0.1.tgz#4626a1575af00f0c5daba41ebc8f8e29a0a1998a" + integrity sha512-EhFm1AxoSBK3MflkFawZ4jmOX1dWu0nBAtCpvGxGsondEvCpsohbpRpM8pi8UAcxG5eRsDsCiRcxdH20j3Rp9A== + +stats.js@^0.17.0: + version "0.17.0" + resolved "https://registry.yarnpkg.com/stats.js/-/stats.js-0.17.0.tgz#b1c3dc46d94498b578b7fd3985b81ace7131cc7d" + integrity sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw== + stop-iteration-iterator@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" @@ -6057,6 +6374,11 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +suspend-react@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/suspend-react/-/suspend-react-0.1.3.tgz#a52f49d21cfae9a2fb70bd0c68413d3f9d90768e" + integrity sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ== + symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" @@ -6109,6 +6431,28 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== +three-mesh-bvh@^0.7.0: + version "0.7.3" + resolved "https://registry.yarnpkg.com/three-mesh-bvh/-/three-mesh-bvh-0.7.3.tgz#91f2d7e26f230288b5b0a6bdf41bdd9620348945" + integrity sha512-3W6KjzmupjfE89GuHPT31kxKWZ4YGZPEZJNysJpiOZfQRsBQQgmK7v/VJPpjG6syhAvTnY+5Fr77EvIkTLpGSw== + +three-stdlib@^2.21.1, three-stdlib@^2.29.4: + version "2.29.4" + resolved "https://registry.yarnpkg.com/three-stdlib/-/three-stdlib-2.29.4.tgz#6e8741f6a2d435d15ed73f3a14dd149660d4ce51" + integrity sha512-XNzGCrz/uAk9XoLwd35eN7dQyI4ggXZTeqjcN034YdYBpBlNO9kmLHehl/0Nw9jCelblB7jla+unHAOIyLyV6Q== + dependencies: + "@types/draco3d" "^1.4.0" + "@types/offscreencanvas" "^2019.6.4" + "@types/webxr" "^0.5.2" + draco3d "^1.4.1" + fflate "^0.6.9" + potpack "^1.0.1" + +three@^0.161.0: + version "0.161.0" + resolved "https://registry.yarnpkg.com/three/-/three-0.161.0.tgz#38aafaa82fe5467fde2e33933515d1b6beb17d91" + integrity sha512-LC28VFtjbOyEu5b93K0bNRLw1rQlMJ85lilKsYj6dgTu+7i17W+JCCEbvrpmNHF1F3NAUqDSWq50UD7w9H2xQw== + through2@^0.6.3: version "0.6.5" resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48" @@ -6167,6 +6511,26 @@ tr46@^3.0.0: dependencies: punycode "^2.1.1" +troika-three-text@^0.49.0: + version "0.49.0" + resolved "https://registry.yarnpkg.com/troika-three-text/-/troika-three-text-0.49.0.tgz#bc2dc1924250c477cd39316cd83585dee12550e0" + integrity sha512-sn9BNC6eIX8OO3iAkPwjecJ7Pn21Ve8P1UNFMNeQzXx759rrqS4i4pSZs7FLMYdWyCKVXBFGimBySFwRKLjq/Q== + dependencies: + bidi-js "^1.0.2" + troika-three-utils "^0.49.0" + troika-worker-utils "^0.49.0" + webgl-sdf-generator "1.1.1" + +troika-three-utils@^0.49.0: + version "0.49.0" + resolved "https://registry.yarnpkg.com/troika-three-utils/-/troika-three-utils-0.49.0.tgz#3fbdbf8783740ce3c1f7acac642e7e957ea4f090" + integrity sha512-umitFL4cT+Fm/uONmaQEq4oZlyRHWwVClaS6ZrdcueRvwc2w+cpNQ47LlJKJswpqtMFWbEhOLy0TekmcPZOdYA== + +troika-worker-utils@^0.49.0: + version "0.49.0" + resolved "https://registry.yarnpkg.com/troika-worker-utils/-/troika-worker-utils-0.49.0.tgz#e5e200a09d2e0e4eb9fe818a83effa912e2ec4b4" + integrity sha512-1xZHoJrG0HFfCvT/iyN41DvI/nRykiBtHqFkGaGgJwq5iXfIZFBiPPEHFpPpgyKM3Oo5ITHXP5wM2TNQszYdVg== + ts-api-utils@^1.0.1: version "1.3.0" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" @@ -6220,6 +6584,13 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +tunnel-rat@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/tunnel-rat/-/tunnel-rat-0.1.2.tgz#1717efbc474ea2d8aa05a91622457a6e201c0aeb" + integrity sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ== + dependencies: + zustand "^4.3.2" + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -6427,7 +6798,7 @@ url@^0.11.3: punycode "^1.4.1" qs "^6.11.2" -use-sync-external-store@^1.2.0: +use-sync-external-store@1.2.0, use-sync-external-store@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== @@ -6444,6 +6815,16 @@ util@^0.10.3: dependencies: inherits "2.0.3" +utility-types@^3.10.0: + version "3.11.0" + resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.11.0.tgz#607c40edb4f258915e901ea7995607fdf319424c" + integrity sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw== + +uuid@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + v8-to-istanbul@^9.0.1: version "9.1.0" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz#1b83ed4e397f58c85c266a570fc2558b5feb9265" @@ -6495,6 +6876,16 @@ warning@^4.0.2: dependencies: loose-envify "^1.0.0" +webgl-constants@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/webgl-constants/-/webgl-constants-1.1.1.tgz#f9633ee87fea56647a60b9ce735cbdfb891c6855" + integrity sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg== + +webgl-sdf-generator@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz#3e1b422b3d87cd3cc77f2602c9db63bc0f6accbd" + integrity sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA== + webgpu@^0.1.16: version "0.1.16" resolved "https://registry.yarnpkg.com/webgpu/-/webgpu-0.1.16.tgz#dec416373e308181b28864b58c8a914461d7ceee" @@ -6694,3 +7085,15 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zustand@^3.7.1: + version "3.7.2" + resolved "https://registry.yarnpkg.com/zustand/-/zustand-3.7.2.tgz#7b44c4f4a5bfd7a8296a3957b13e1c346f42514d" + integrity sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA== + +zustand@^4.3.2: + version "4.5.1" + resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.1.tgz#2088956ee454759fb8b866ca335a2373e76736c5" + integrity sha512-XlauQmH64xXSC1qGYNv00ODaQ3B+tNPoy22jv2diYiP4eoDKr9LA+Bh5Bc3gplTrFdb6JVI+N4kc1DZ/tbtfPg== + dependencies: + use-sync-external-store "1.2.0"