angular-three 2.0.0-beta.273 → 2.0.0-beta.275

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/esm2022/index.mjs +5 -5
  2. package/esm2022/lib/canvas.mjs +2 -1
  3. package/esm2022/lib/dom/events.mjs +1 -1
  4. package/esm2022/lib/events.mjs +1 -1
  5. package/esm2022/lib/instance.mjs +1 -1
  6. package/esm2022/lib/loop.mjs +2 -2
  7. package/esm2022/lib/portal.mjs +1 -1
  8. package/esm2022/lib/renderer/index.mjs +1 -1
  9. package/esm2022/lib/renderer/utils.mjs +1 -1
  10. package/esm2022/lib/roots.mjs +2 -3
  11. package/esm2022/lib/store.mjs +1 -1
  12. package/esm2022/lib/three-types.mjs +1 -1
  13. package/esm2022/lib/types.mjs +1 -1
  14. package/esm2022/lib/utils/apply-props.mjs +1 -1
  15. package/esm2022/lib/utils/attach.mjs +1 -1
  16. package/esm2022/lib/utils/before-render.mjs +1 -1
  17. package/esm2022/lib/utils/is.mjs +1 -1
  18. package/esm2022/lib/utils/make.mjs +1 -1
  19. package/esm2022/lib/utils/object-events.mjs +1 -1
  20. package/esm2022/lib/utils/update.mjs +1 -1
  21. package/esm2022/testing/lib/test-bed.mjs +12 -6
  22. package/fesm2022/angular-three-testing.mjs +10 -4
  23. package/fesm2022/angular-three-testing.mjs.map +1 -1
  24. package/fesm2022/angular-three.mjs +528 -527
  25. package/fesm2022/angular-three.mjs.map +1 -1
  26. package/index.d.ts +4 -5
  27. package/lib/canvas.d.ts +199 -256
  28. package/lib/dom/events.d.ts +1 -2
  29. package/lib/events.d.ts +2 -78
  30. package/lib/html.d.ts +1 -1
  31. package/lib/instance.d.ts +1 -38
  32. package/lib/loop.d.ts +2 -2
  33. package/lib/portal.d.ts +3 -4
  34. package/lib/renderer/index.d.ts +1 -2
  35. package/lib/renderer/utils.d.ts +1 -1
  36. package/lib/roots.d.ts +1 -5
  37. package/lib/store.d.ts +2 -127
  38. package/lib/three-types.d.ts +1 -3
  39. package/lib/types.d.ts +290 -0
  40. package/lib/utils/apply-props.d.ts +1 -2
  41. package/lib/utils/attach.d.ts +1 -3
  42. package/lib/utils/before-render.d.ts +1 -1
  43. package/lib/utils/is.d.ts +1 -3
  44. package/lib/utils/make.d.ts +2 -5
  45. package/lib/utils/object-events.d.ts +1 -1
  46. package/lib/utils/update.d.ts +1 -1
  47. package/package.json +1 -1
@@ -3,7 +3,7 @@ import { untracked, computed, signal, ElementRef, input, inject, ViewContainerRe
3
3
  import { takeUntilDestroyed, outputFromObservable } from '@angular/core/rxjs-interop';
4
4
  import { injectAutoEffect } from 'ngxtension/auto-effect';
5
5
  import { provideResizeOptions, NgxResize } from 'ngxtension/resize';
6
- import { MathUtils, WebGLRenderer, OrthographicCamera, PerspectiveCamera, Vector3, Layers, Color, ColorManagement, Texture, RGBAFormat, UnsignedByteType, Raycaster, Scene, PCFSoftShadowMap, BasicShadowMap, PCFShadowMap, VSMShadowMap, NoToneMapping, ACESFilmicToneMapping, Vector2, Clock } from 'three';
6
+ import { MathUtils, WebGLRenderer, OrthographicCamera, PerspectiveCamera, Vector3, Vector2, Clock, Layers, Color, ColorManagement, Texture, RGBAFormat, UnsignedByteType, Raycaster, Scene, PCFSoftShadowMap, BasicShadowMap, PCFShadowMap, VSMShadowMap, NoToneMapping, ACESFilmicToneMapping } from 'three';
7
7
  import { DOCUMENT } from '@angular/common';
8
8
  import { Subject, filter } from 'rxjs';
9
9
  import { createInjectionToken } from 'ngxtension/create-injection-token';
@@ -814,526 +814,145 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.2", ngImpor
814
814
  args: [{ selector: 'ng-template[args]', standalone: true }]
815
815
  }], ctorParameters: () => [] });
816
816
 
817
- // This function prepares a set of changes to be applied to the instance
818
- function diffProps(instance, props) {
819
- const propsEntries = Object.entries(props);
820
- const changes = [];
821
- for (const [propKey, propValue] of propsEntries) {
822
- let key = propKey;
823
- if (is.colorSpaceExist(instance)) {
824
- if (propKey === 'encoding') {
825
- key = 'colorSpace';
826
- }
827
- else if (propKey === 'outputEncoding') {
828
- key = 'outputColorSpace';
829
- }
830
- }
831
- if (is.equ(propValue, instance[key]))
832
- continue;
833
- changes.push([propKey, propValue]);
817
+ const roots = new Map();
818
+ function createSubs(callback, subs) {
819
+ const sub = { callback };
820
+ subs.add(sub);
821
+ return () => void subs.delete(sub);
822
+ }
823
+ const globalEffects = new Set();
824
+ const globalAfterEffects = new Set();
825
+ const globalTailEffects = new Set();
826
+ /**
827
+ * Adds a global render callback which is called each frame.
828
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addEffect
829
+ */
830
+ const addEffect = (callback) => createSubs(callback, globalEffects);
831
+ /**
832
+ * Adds a global after-render callback which is called each frame.
833
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addAfterEffect
834
+ */
835
+ const addAfterEffect = (callback) => createSubs(callback, globalAfterEffects);
836
+ /**
837
+ * Adds a global callback which is called when rendering stops.
838
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addTail
839
+ */
840
+ const addTail = (callback) => createSubs(callback, globalTailEffects);
841
+ function run(effects, timestamp) {
842
+ if (!effects.size)
843
+ return;
844
+ for (const { callback } of effects.values()) {
845
+ callback(timestamp);
834
846
  }
835
- return changes;
836
847
  }
837
- // This function applies a set of changes to the instance
838
- function applyProps(instance, props) {
839
- // if props is empty
840
- if (!Object.keys(props).length)
841
- return instance;
842
- // filter equals, and reserved props
843
- const localState = getLocalState(instance);
844
- const rootState = localState?.store?.snapshot ?? {};
845
- const changes = diffProps(instance, props);
846
- for (let i = 0; i < changes.length; i++) {
847
- let [key, value] = changes[i];
848
- // Alias (output)encoding => (output)colorSpace (since r152)
849
- // https://github.com/pmndrs/react-three-fiber/pull/2829
850
- if (is.colorSpaceExist(instance)) {
851
- const sRGBEncoding = 3001;
852
- const SRGBColorSpace = 'srgb';
853
- const LinearSRGBColorSpace = 'srgb-linear';
854
- if (key === 'encoding') {
855
- key = 'colorSpace';
856
- value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
857
- }
858
- else if (key === 'outputEncoding') {
859
- key = 'outputColorSpace';
860
- value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
848
+ function flushGlobalEffects(type, timestamp) {
849
+ switch (type) {
850
+ case 'before':
851
+ return run(globalEffects, timestamp);
852
+ case 'after':
853
+ return run(globalAfterEffects, timestamp);
854
+ case 'tail':
855
+ return run(globalTailEffects, timestamp);
856
+ }
857
+ }
858
+ function render(timestamp, store, frame) {
859
+ const state = store.snapshot;
860
+ // Run local effects
861
+ let delta = state.clock.getDelta();
862
+ // In frameloop='never' mode, clock times are updated using the provided timestamp
863
+ if (state.frameloop === 'never' && typeof timestamp === 'number') {
864
+ delta = timestamp - state.clock.elapsedTime;
865
+ state.clock.oldTime = state.clock.elapsedTime;
866
+ state.clock.elapsedTime = timestamp;
867
+ }
868
+ // Call subscribers (beforeRender)
869
+ const subscribers = state.internal.subscribers;
870
+ for (let i = 0; i < subscribers.length; i++) {
871
+ const subscription = subscribers[i];
872
+ subscription.callback({ ...subscription.store.snapshot, delta, frame });
873
+ }
874
+ // Render content
875
+ if (!state.internal.priority && state.gl.render)
876
+ state.gl.render(state.scene, state.camera);
877
+ // Decrease frame count
878
+ state.internal.frames = Math.max(0, state.internal.frames - 1);
879
+ return state.frameloop === 'always' ? 1 : state.internal.frames;
880
+ }
881
+ function createLoop(roots) {
882
+ let running = false;
883
+ let repeat;
884
+ let frame;
885
+ let beforeRenderInProgress = false;
886
+ function loop(timestamp) {
887
+ frame = requestAnimationFrame(loop);
888
+ running = true;
889
+ repeat = 0;
890
+ // Run effects
891
+ flushGlobalEffects('before', timestamp);
892
+ // Render all roots
893
+ beforeRenderInProgress = true;
894
+ for (const root of roots.values()) {
895
+ const state = root.snapshot;
896
+ // If the frameloop is invalidated, do not run another frame
897
+ if (state.internal.active &&
898
+ (state.frameloop === 'always' || state.internal.frames > 0) &&
899
+ !state.gl.xr?.isPresenting) {
900
+ repeat += render(timestamp, root);
861
901
  }
862
902
  }
863
- const currentInstance = instance;
864
- const targetProp = currentInstance[key];
865
- // special treatmen for objects with support for set/copy, and layers
866
- if (targetProp && targetProp['set'] && (targetProp['copy'] || targetProp instanceof Layers)) {
867
- const isColor = targetProp instanceof Color;
868
- // if value is an array
869
- if (Array.isArray(value)) {
870
- if (targetProp['fromArray'])
871
- targetProp['fromArray'](value);
872
- else
873
- targetProp['set'](...value);
874
- } // test again target.copy
875
- else if (targetProp['copy'] &&
876
- value &&
877
- value.constructor &&
878
- targetProp.constructor.name === value.constructor.name) {
879
- targetProp['copy'](value);
880
- if (!ColorManagement && !rootState.linear && isColor)
881
- targetProp['convertSRGBToLinear']();
882
- } // if nothing else fits, just set the single value, ignore undefined
883
- else if (value !== undefined) {
884
- const isColor = targetProp instanceof Color;
885
- // allow setting array scalars
886
- if (!isColor && targetProp['setScalar'])
887
- targetProp['setScalar'](value);
888
- // layers have no copy function, copy the mask
889
- else if (targetProp instanceof Layers && value instanceof Layers)
890
- targetProp.mask = value.mask;
891
- // otherwise just set ...
892
- else
893
- targetProp['set'](value);
894
- // auto-convert srgb
895
- if (!ColorManagement && !rootState?.linear && isColor)
896
- targetProp.convertSRGBToLinear();
897
- }
898
- } // else just overwrite the value
903
+ beforeRenderInProgress = false;
904
+ // Run after-effects
905
+ flushGlobalEffects('after', timestamp);
906
+ // Stop the loop if nothing invalidates it
907
+ if (repeat === 0) {
908
+ // Tail call effects, they are called when rendering stops
909
+ flushGlobalEffects('tail', timestamp);
910
+ // Flag end of operation
911
+ running = false;
912
+ return cancelAnimationFrame(frame);
913
+ }
914
+ }
915
+ function invalidate(store, frames = 1) {
916
+ const state = store?.snapshot;
917
+ if (!state)
918
+ return roots.forEach((root) => invalidate(root, frames));
919
+ if (state.gl.xr?.isPresenting || !state.internal.active || state.frameloop === 'never')
920
+ return;
921
+ if (frames > 1) {
922
+ // legacy support for people using frames parameters
923
+ // Increase frames, do not go higher than 60
924
+ state.internal.frames = Math.min(60, state.internal.frames + frames);
925
+ }
899
926
  else {
900
- currentInstance[key] = value;
901
- // auto-convert srgb textures
902
- if (currentInstance[key] instanceof Texture &&
903
- currentInstance[key].format === RGBAFormat &&
904
- currentInstance[key].type === UnsignedByteType) {
905
- const texture = currentInstance[key];
906
- if (rootState?.gl) {
907
- if (is.colorSpaceExist(texture) && is.colorSpaceExist(rootState.gl))
908
- texture.colorSpace = rootState.gl.outputColorSpace;
909
- // @ts-expect-error - old version of threejs
910
- else
911
- texture.encoding = rootState.gl.outputEncoding;
912
- }
927
+ if (beforeRenderInProgress) {
928
+ //called from within a beforeRender, it means the user wants an additional frame
929
+ state.internal.frames = 2;
930
+ }
931
+ else {
932
+ //the user need a new frame, no need to increment further than 1
933
+ state.internal.frames = 1;
913
934
  }
914
935
  }
915
- checkUpdate(currentInstance[key]);
916
- checkUpdate(targetProp);
917
- invalidateInstance(instance);
918
- }
919
- const instanceHandlersCount = localState?.eventCount;
920
- const parent = localState?.instanceStore?.get('parent');
921
- if (parent && rootState.internal && instance['raycast'] && instanceHandlersCount !== localState?.eventCount) {
922
- // Pre-emptively remove the instance from the interaction manager
923
- const index = rootState.internal.interaction.indexOf(instance);
924
- if (index > -1)
925
- rootState.internal.interaction.splice(index, 1);
926
- // Add the instance to the interaction manager only when it has handlers
927
- if (localState?.eventCount)
928
- rootState.internal.interaction.push(instance);
936
+ // If the render-loop isn't active, start it
937
+ if (!running) {
938
+ running = true;
939
+ requestAnimationFrame(loop);
940
+ }
929
941
  }
930
- if (parent && localState?.onUpdate && changes.length) {
931
- localState.onUpdate(instance);
942
+ function advance(timestamp, runGlobalEffects = true, store, frame) {
943
+ if (runGlobalEffects)
944
+ flushGlobalEffects('before', timestamp);
945
+ if (!store)
946
+ for (const root of roots.values())
947
+ render(timestamp, root);
948
+ else
949
+ render(timestamp, store, frame);
950
+ if (runGlobalEffects)
951
+ flushGlobalEffects('after', timestamp);
932
952
  }
933
- return instance;
953
+ return { loop, invalidate, advance };
934
954
  }
935
-
936
- const shallowLoose = { objects: 'shallow', strict: false };
937
- const roots = new Map();
938
- function injectCanvasRootInitializer(injector) {
939
- return assertInjector(injectCanvasRootInitializer, injector, () => {
940
- const injectedStore = injectStore();
941
- const loop = injectLoop();
942
- return (canvas) => {
943
- const exist = roots.has(canvas);
944
- let store = roots.get(canvas);
945
- if (store) {
946
- console.warn('[NGT] Same canvas root is being created twice');
947
- }
948
- store ||= injectedStore;
949
- if (!store) {
950
- throw new Error('[NGT] No store initialized');
951
- }
952
- if (!exist) {
953
- roots.set(canvas, store);
954
- }
955
- let isConfigured = false;
956
- let lastCamera;
957
- return {
958
- isConfigured,
959
- destroy: (timeout = 500) => {
960
- const root = roots.get(canvas);
961
- if (root) {
962
- root.update((state) => ({ internal: { ...state.internal, active: false } }));
963
- try {
964
- const state = root.get();
965
- state.events.disconnect?.();
966
- state.gl?.renderLists?.dispose?.();
967
- state.gl?.forceContextLoss?.();
968
- if (state.gl?.xr)
969
- state.xr.disconnect();
970
- dispose(state);
971
- roots.delete(canvas);
972
- }
973
- catch (e) {
974
- console.error('[NGT] Unexpected error while destroying Canvas Root', e);
975
- }
976
- }
977
- },
978
- configure: (inputs) => {
979
- const { shadows = false, linear = false, flat = false, legacy = false, orthographic = false, frameloop = 'always', dpr = [1, 2], gl: glOptions, size: sizeOptions, camera: cameraOptions, raycaster: raycasterOptions, scene: sceneOptions, events, lookAt, performance, } = inputs;
980
- const state = store.snapshot;
981
- const stateToUpdate = {};
982
- // setup renderer
983
- let gl = state.gl;
984
- if (!state.gl)
985
- stateToUpdate.gl = gl = makeRendererInstance(glOptions, canvas);
986
- // setup raycaster
987
- let raycaster = state.raycaster;
988
- if (!raycaster)
989
- stateToUpdate.raycaster = raycaster = new Raycaster();
990
- // set raycaster options
991
- const { params, ...options } = raycasterOptions || {};
992
- if (!is.equ(options, raycaster, shallowLoose))
993
- applyProps(raycaster, options);
994
- if (!is.equ(params, raycaster.params, shallowLoose)) {
995
- applyProps(raycaster, { params: { ...raycaster.params, ...(params || {}) } });
996
- }
997
- // Create default camera, don't overwrite any user-set state
998
- if (!state.camera || (state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose))) {
999
- lastCamera = cameraOptions;
1000
- const isCamera = is.camera(cameraOptions);
1001
- let camera = isCamera ? cameraOptions : makeCameraInstance(orthographic, state.size);
1002
- if (!isCamera) {
1003
- camera.position.z = 5;
1004
- if (cameraOptions) {
1005
- applyProps(camera, cameraOptions);
1006
- if ('aspect' in cameraOptions ||
1007
- 'left' in cameraOptions ||
1008
- 'right' in cameraOptions ||
1009
- 'top' in cameraOptions ||
1010
- 'bottom' in cameraOptions) {
1011
- Object.assign(camera, { manual: true });
1012
- camera?.updateProjectionMatrix();
1013
- }
1014
- }
1015
- // always look at center or passed-in lookAt by default
1016
- if (!state.camera && !cameraOptions?.rotation && !cameraOptions?.quaternion) {
1017
- if (Array.isArray(lookAt))
1018
- camera.lookAt(lookAt[0], lookAt[1], lookAt[2]);
1019
- else if (lookAt instanceof Vector3)
1020
- camera.lookAt(lookAt);
1021
- else
1022
- camera.lookAt(0, 0, 0);
1023
- }
1024
- // update projection matrix after applyprops
1025
- camera.updateProjectionMatrix?.();
1026
- }
1027
- if (!is.instance(camera))
1028
- camera = prepare(camera, { store });
1029
- stateToUpdate.camera = camera;
1030
- // Configure raycaster
1031
- // https://github.com/pmndrs/react-xr/issues/300
1032
- raycaster.camera = camera;
1033
- }
1034
- // Set up scene (one time only!)
1035
- if (!state.scene) {
1036
- let scene;
1037
- if (sceneOptions instanceof Scene) {
1038
- scene = sceneOptions;
1039
- }
1040
- else {
1041
- scene = new Scene();
1042
- if (sceneOptions)
1043
- applyProps(scene, sceneOptions);
1044
- }
1045
- applyProps(scene, {
1046
- setAttribute: (name, value) => {
1047
- if (canvas instanceof HTMLCanvasElement) {
1048
- if (canvas.parentElement) {
1049
- canvas.parentElement.setAttribute(name, value);
1050
- }
1051
- else {
1052
- canvas.setAttribute(name, value);
1053
- }
1054
- }
1055
- },
1056
- });
1057
- stateToUpdate.scene = prepare(scene, { store });
1058
- }
1059
- // Set up XR (one time only!)
1060
- if (!state.xr) {
1061
- // Handle frame behavior in WebXR
1062
- const handleXRFrame = (timestamp, frame) => {
1063
- const state = store.snapshot;
1064
- if (state.frameloop === 'never')
1065
- return;
1066
- loop.advance(timestamp, true, store, frame);
1067
- };
1068
- // Toggle render switching on session
1069
- const handleSessionChange = () => {
1070
- const state = store.snapshot;
1071
- state.gl.xr.enabled = state.gl.xr.isPresenting;
1072
- state.gl.xr.setAnimationLoop(state.gl.xr.isPresenting ? handleXRFrame : null);
1073
- if (!state.gl.xr.isPresenting)
1074
- loop.invalidate(store);
1075
- };
1076
- // WebXR session manager
1077
- const xr = {
1078
- connect: () => {
1079
- gl.xr.addEventListener('sessionstart', handleSessionChange);
1080
- gl.xr.addEventListener('sessionend', handleSessionChange);
1081
- },
1082
- disconnect: () => {
1083
- gl.xr.removeEventListener('sessionstart', handleSessionChange);
1084
- gl.xr.removeEventListener('sessionend', handleSessionChange);
1085
- },
1086
- };
1087
- // Subscribe to WebXR session events
1088
- if (gl.xr && typeof gl.xr.addEventListener === 'function')
1089
- xr.connect();
1090
- stateToUpdate.xr = xr;
1091
- }
1092
- // Set shadowmap
1093
- if (gl.shadowMap) {
1094
- const oldEnabled = gl.shadowMap.enabled;
1095
- const oldType = gl.shadowMap.type;
1096
- gl.shadowMap.enabled = !!shadows;
1097
- if (typeof shadows === 'boolean') {
1098
- gl.shadowMap.type = PCFSoftShadowMap;
1099
- }
1100
- else if (typeof shadows === 'string') {
1101
- const types = {
1102
- basic: BasicShadowMap,
1103
- percentage: PCFShadowMap,
1104
- soft: PCFSoftShadowMap,
1105
- variance: VSMShadowMap,
1106
- };
1107
- gl.shadowMap.type = types[shadows] ?? PCFSoftShadowMap;
1108
- }
1109
- else if (is.obj(shadows)) {
1110
- Object.assign(gl.shadowMap, shadows);
1111
- }
1112
- if (oldEnabled !== gl.shadowMap.enabled || oldType !== gl.shadowMap.type)
1113
- checkNeedsUpdate(gl.shadowMap);
1114
- }
1115
- // Safely set color management if available.
1116
- // Avoid accessing ColorManagement to play nice with older versions
1117
- if (ColorManagement) {
1118
- const colorManagement = ColorManagement;
1119
- if ('enabled' in colorManagement)
1120
- colorManagement['enabled'] = !legacy ?? false;
1121
- else if ('legacyMode' in colorManagement)
1122
- colorManagement['legacyMode'] = legacy ?? true;
1123
- }
1124
- if (!isConfigured) {
1125
- // set color space and tonemapping preferences once
1126
- const LinearEncoding = 3000;
1127
- const sRGBEncoding = 3001;
1128
- applyProps(gl, {
1129
- outputEncoding: linear ? LinearEncoding : sRGBEncoding,
1130
- toneMapping: flat ? NoToneMapping : ACESFilmicToneMapping,
1131
- });
1132
- }
1133
- // Update color management state
1134
- if (state.legacy !== legacy)
1135
- stateToUpdate.legacy = legacy;
1136
- if (state.linear !== linear)
1137
- stateToUpdate.linear = linear;
1138
- if (state.flat !== flat)
1139
- stateToUpdate.flat = flat;
1140
- // Set gl props
1141
- gl.setClearAlpha(0);
1142
- gl.setPixelRatio(makeDpr(state.viewport.dpr));
1143
- gl.setSize(state.size.width, state.size.height);
1144
- if (is.obj(glOptions) &&
1145
- !(typeof glOptions === 'function') &&
1146
- !is.renderer(glOptions) &&
1147
- !is.equ(glOptions, gl, shallowLoose)) {
1148
- applyProps(gl, glOptions);
1149
- }
1150
- // Store events internally
1151
- if (events && !state.events.handlers)
1152
- stateToUpdate.events = events(store);
1153
- // Check performance
1154
- if (performance && !is.equ(performance, state.performance, shallowLoose)) {
1155
- stateToUpdate.performance = { ...state.performance, ...performance };
1156
- }
1157
- if (Object.keys(stateToUpdate).length) {
1158
- store.update(stateToUpdate);
1159
- }
1160
- // Check size, allow it to take on container bounds initially
1161
- const size = computeInitialSize(canvas, sizeOptions);
1162
- if (!is.equ(size, state.size, shallowLoose)) {
1163
- state.setSize(size.width, size.height, size.top, size.left);
1164
- }
1165
- // Check pixelratio
1166
- if (dpr && state.viewport.dpr !== makeDpr(dpr))
1167
- state.setDpr(dpr);
1168
- // Check frameloop
1169
- if (state.frameloop !== frameloop)
1170
- state.setFrameloop(frameloop);
1171
- isConfigured = true;
1172
- },
1173
- };
1174
- };
1175
- });
1176
- }
1177
- function computeInitialSize(canvas, defaultSize) {
1178
- if (defaultSize) {
1179
- return defaultSize;
1180
- }
1181
- if (typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
1182
- return canvas.parentElement.getBoundingClientRect();
1183
- }
1184
- if (typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas) {
1185
- return { width: canvas.width, height: canvas.height, top: 0, left: 0 };
1186
- }
1187
- return { width: 0, height: 0, top: 0, left: 0 };
1188
- }
1189
- // Disposes an object and all its properties
1190
- function dispose(obj) {
1191
- if (obj.dispose && obj.type !== 'Scene')
1192
- obj.dispose();
1193
- for (const p in obj) {
1194
- p.dispose?.();
1195
- delete obj[p];
1196
- }
1197
- }
1198
-
1199
- function createSubs(callback, subs) {
1200
- const sub = { callback };
1201
- subs.add(sub);
1202
- return () => void subs.delete(sub);
1203
- }
1204
- const globalEffects = new Set();
1205
- const globalAfterEffects = new Set();
1206
- const globalTailEffects = new Set();
1207
- /**
1208
- * Adds a global render callback which is called each frame.
1209
- * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addEffect
1210
- */
1211
- const addEffect = (callback) => createSubs(callback, globalEffects);
1212
- /**
1213
- * Adds a global after-render callback which is called each frame.
1214
- * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addAfterEffect
1215
- */
1216
- const addAfterEffect = (callback) => createSubs(callback, globalAfterEffects);
1217
- /**
1218
- * Adds a global callback which is called when rendering stops.
1219
- * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addTail
1220
- */
1221
- const addTail = (callback) => createSubs(callback, globalTailEffects);
1222
- function run(effects, timestamp) {
1223
- if (!effects.size)
1224
- return;
1225
- for (const { callback } of effects.values()) {
1226
- callback(timestamp);
1227
- }
1228
- }
1229
- function flushGlobalEffects(type, timestamp) {
1230
- switch (type) {
1231
- case 'before':
1232
- return run(globalEffects, timestamp);
1233
- case 'after':
1234
- return run(globalAfterEffects, timestamp);
1235
- case 'tail':
1236
- return run(globalTailEffects, timestamp);
1237
- }
1238
- }
1239
- function render(timestamp, store, frame) {
1240
- const state = store.snapshot;
1241
- // Run local effects
1242
- let delta = state.clock.getDelta();
1243
- // In frameloop='never' mode, clock times are updated using the provided timestamp
1244
- if (state.frameloop === 'never' && typeof timestamp === 'number') {
1245
- delta = timestamp - state.clock.elapsedTime;
1246
- state.clock.oldTime = state.clock.elapsedTime;
1247
- state.clock.elapsedTime = timestamp;
1248
- }
1249
- // Call subscribers (beforeRender)
1250
- const subscribers = state.internal.subscribers;
1251
- for (let i = 0; i < subscribers.length; i++) {
1252
- const subscription = subscribers[i];
1253
- subscription.callback({ ...subscription.store.snapshot, delta, frame });
1254
- }
1255
- // Render content
1256
- if (!state.internal.priority && state.gl.render)
1257
- state.gl.render(state.scene, state.camera);
1258
- // Decrease frame count
1259
- state.internal.frames = Math.max(0, state.internal.frames - 1);
1260
- return state.frameloop === 'always' ? 1 : state.internal.frames;
1261
- }
1262
- function createLoop(roots) {
1263
- let running = false;
1264
- let repeat;
1265
- let frame;
1266
- let beforeRenderInProgress = false;
1267
- function loop(timestamp) {
1268
- frame = requestAnimationFrame(loop);
1269
- running = true;
1270
- repeat = 0;
1271
- // Run effects
1272
- flushGlobalEffects('before', timestamp);
1273
- // Render all roots
1274
- beforeRenderInProgress = true;
1275
- for (const root of roots.values()) {
1276
- const state = root.snapshot;
1277
- // If the frameloop is invalidated, do not run another frame
1278
- if (state.internal.active &&
1279
- (state.frameloop === 'always' || state.internal.frames > 0) &&
1280
- !state.gl.xr?.isPresenting) {
1281
- repeat += render(timestamp, root);
1282
- }
1283
- }
1284
- beforeRenderInProgress = false;
1285
- // Run after-effects
1286
- flushGlobalEffects('after', timestamp);
1287
- // Stop the loop if nothing invalidates it
1288
- if (repeat === 0) {
1289
- // Tail call effects, they are called when rendering stops
1290
- flushGlobalEffects('tail', timestamp);
1291
- // Flag end of operation
1292
- running = false;
1293
- return cancelAnimationFrame(frame);
1294
- }
1295
- }
1296
- function invalidate(store, frames = 1) {
1297
- const state = store?.snapshot;
1298
- if (!state)
1299
- return roots.forEach((root) => invalidate(root, frames));
1300
- if (state.gl.xr?.isPresenting || !state.internal.active || state.frameloop === 'never')
1301
- return;
1302
- if (frames > 1) {
1303
- // legacy support for people using frames parameters
1304
- // Increase frames, do not go higher than 60
1305
- state.internal.frames = Math.min(60, state.internal.frames + frames);
1306
- }
1307
- else {
1308
- if (beforeRenderInProgress) {
1309
- //called from within a beforeRender, it means the user wants an additional frame
1310
- state.internal.frames = 2;
1311
- }
1312
- else {
1313
- //the user need a new frame, no need to increment further than 1
1314
- state.internal.frames = 1;
1315
- }
1316
- }
1317
- // If the render-loop isn't active, start it
1318
- if (!running) {
1319
- running = true;
1320
- requestAnimationFrame(loop);
1321
- }
1322
- }
1323
- function advance(timestamp, runGlobalEffects = true, store, frame) {
1324
- if (runGlobalEffects)
1325
- flushGlobalEffects('before', timestamp);
1326
- if (!store)
1327
- for (const root of roots.values())
1328
- render(timestamp, root);
1329
- else
1330
- render(timestamp, store, frame);
1331
- if (runGlobalEffects)
1332
- flushGlobalEffects('after', timestamp);
1333
- }
1334
- return { loop, invalidate, advance };
1335
- }
1336
- const [injectLoop] = createInjectionToken(() => createLoop(roots));
955
+ const [injectLoop] = createInjectionToken(() => createLoop(roots));
1337
956
 
1338
957
  function storeFactory(previousStore) {
1339
958
  const document = inject(DOCUMENT);
@@ -1509,6 +1128,125 @@ function injectStore(options) {
1509
1128
  return inject(NGT_STORE, options);
1510
1129
  }
1511
1130
 
1131
+ // This function prepares a set of changes to be applied to the instance
1132
+ function diffProps(instance, props) {
1133
+ const propsEntries = Object.entries(props);
1134
+ const changes = [];
1135
+ for (const [propKey, propValue] of propsEntries) {
1136
+ let key = propKey;
1137
+ if (is.colorSpaceExist(instance)) {
1138
+ if (propKey === 'encoding') {
1139
+ key = 'colorSpace';
1140
+ }
1141
+ else if (propKey === 'outputEncoding') {
1142
+ key = 'outputColorSpace';
1143
+ }
1144
+ }
1145
+ if (is.equ(propValue, instance[key]))
1146
+ continue;
1147
+ changes.push([propKey, propValue]);
1148
+ }
1149
+ return changes;
1150
+ }
1151
+ // This function applies a set of changes to the instance
1152
+ function applyProps(instance, props) {
1153
+ // if props is empty
1154
+ if (!Object.keys(props).length)
1155
+ return instance;
1156
+ // filter equals, and reserved props
1157
+ const localState = getLocalState(instance);
1158
+ const rootState = localState?.store?.snapshot ?? {};
1159
+ const changes = diffProps(instance, props);
1160
+ for (let i = 0; i < changes.length; i++) {
1161
+ let [key, value] = changes[i];
1162
+ // Alias (output)encoding => (output)colorSpace (since r152)
1163
+ // https://github.com/pmndrs/react-three-fiber/pull/2829
1164
+ if (is.colorSpaceExist(instance)) {
1165
+ const sRGBEncoding = 3001;
1166
+ const SRGBColorSpace = 'srgb';
1167
+ const LinearSRGBColorSpace = 'srgb-linear';
1168
+ if (key === 'encoding') {
1169
+ key = 'colorSpace';
1170
+ value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
1171
+ }
1172
+ else if (key === 'outputEncoding') {
1173
+ key = 'outputColorSpace';
1174
+ value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
1175
+ }
1176
+ }
1177
+ const currentInstance = instance;
1178
+ const targetProp = currentInstance[key];
1179
+ // special treatmen for objects with support for set/copy, and layers
1180
+ if (targetProp && targetProp['set'] && (targetProp['copy'] || targetProp instanceof Layers)) {
1181
+ const isColor = targetProp instanceof Color;
1182
+ // if value is an array
1183
+ if (Array.isArray(value)) {
1184
+ if (targetProp['fromArray'])
1185
+ targetProp['fromArray'](value);
1186
+ else
1187
+ targetProp['set'](...value);
1188
+ } // test again target.copy
1189
+ else if (targetProp['copy'] &&
1190
+ value &&
1191
+ value.constructor &&
1192
+ targetProp.constructor.name === value.constructor.name) {
1193
+ targetProp['copy'](value);
1194
+ if (!ColorManagement && !rootState.linear && isColor)
1195
+ targetProp['convertSRGBToLinear']();
1196
+ } // if nothing else fits, just set the single value, ignore undefined
1197
+ else if (value !== undefined) {
1198
+ const isColor = targetProp instanceof Color;
1199
+ // allow setting array scalars
1200
+ if (!isColor && targetProp['setScalar'])
1201
+ targetProp['setScalar'](value);
1202
+ // layers have no copy function, copy the mask
1203
+ else if (targetProp instanceof Layers && value instanceof Layers)
1204
+ targetProp.mask = value.mask;
1205
+ // otherwise just set ...
1206
+ else
1207
+ targetProp['set'](value);
1208
+ // auto-convert srgb
1209
+ if (!ColorManagement && !rootState?.linear && isColor)
1210
+ targetProp.convertSRGBToLinear();
1211
+ }
1212
+ } // else just overwrite the value
1213
+ else {
1214
+ currentInstance[key] = value;
1215
+ // auto-convert srgb textures
1216
+ if (currentInstance[key] instanceof Texture &&
1217
+ currentInstance[key].format === RGBAFormat &&
1218
+ currentInstance[key].type === UnsignedByteType) {
1219
+ const texture = currentInstance[key];
1220
+ if (rootState?.gl) {
1221
+ if (is.colorSpaceExist(texture) && is.colorSpaceExist(rootState.gl))
1222
+ texture.colorSpace = rootState.gl.outputColorSpace;
1223
+ // @ts-expect-error - old version of threejs
1224
+ else
1225
+ texture.encoding = rootState.gl.outputEncoding;
1226
+ }
1227
+ }
1228
+ }
1229
+ checkUpdate(currentInstance[key]);
1230
+ checkUpdate(targetProp);
1231
+ invalidateInstance(instance);
1232
+ }
1233
+ const instanceHandlersCount = localState?.eventCount;
1234
+ const parent = localState?.instanceStore?.get('parent');
1235
+ if (parent && rootState.internal && instance['raycast'] && instanceHandlersCount !== localState?.eventCount) {
1236
+ // Pre-emptively remove the instance from the interaction manager
1237
+ const index = rootState.internal.interaction.indexOf(instance);
1238
+ if (index > -1)
1239
+ rootState.internal.interaction.splice(index, 1);
1240
+ // Add the instance to the interaction manager only when it has handlers
1241
+ if (localState?.eventCount)
1242
+ rootState.internal.interaction.push(instance);
1243
+ }
1244
+ if (parent && localState?.onUpdate && changes.length) {
1245
+ localState.onUpdate(instance);
1246
+ }
1247
+ return instance;
1248
+ }
1249
+
1512
1250
  const catalogue = {};
1513
1251
  function extend(objects) {
1514
1252
  Object.assign(catalogue, objects);
@@ -2294,24 +2032,286 @@ class NgtRenderer {
2294
2032
  store = instance;
2295
2033
  break;
2296
2034
  }
2297
- i--;
2298
- }
2299
- destroyed.forEach((index) => {
2300
- this.portalCommentsNodes.splice(index, 1);
2301
- });
2302
- return store || this.rootStore;
2035
+ i--;
2036
+ }
2037
+ destroyed.forEach((index) => {
2038
+ this.portalCommentsNodes.splice(index, 1);
2039
+ });
2040
+ return store || this.rootStore;
2041
+ }
2042
+ get data() {
2043
+ return this.delegate.data;
2044
+ }
2045
+ }
2046
+ function provideNgtRenderer(store) {
2047
+ const providers = [
2048
+ NgtRendererFactory,
2049
+ { provide: RendererFactory2, useExisting: NgtRendererFactory },
2050
+ provideStore(() => store),
2051
+ ];
2052
+ return makeEnvironmentProviders(providers);
2053
+ }
2054
+
2055
+ const shallowLoose = { objects: 'shallow', strict: false };
2056
+ function injectCanvasRootInitializer(injector) {
2057
+ return assertInjector(injectCanvasRootInitializer, injector, () => {
2058
+ const injectedStore = injectStore();
2059
+ const loop = injectLoop();
2060
+ return (canvas) => {
2061
+ const exist = roots.has(canvas);
2062
+ let store = roots.get(canvas);
2063
+ if (store) {
2064
+ console.warn('[NGT] Same canvas root is being created twice');
2065
+ }
2066
+ store ||= injectedStore;
2067
+ if (!store) {
2068
+ throw new Error('[NGT] No store initialized');
2069
+ }
2070
+ if (!exist) {
2071
+ roots.set(canvas, store);
2072
+ }
2073
+ let isConfigured = false;
2074
+ let lastCamera;
2075
+ return {
2076
+ isConfigured,
2077
+ destroy: (timeout = 500) => {
2078
+ const root = roots.get(canvas);
2079
+ if (root) {
2080
+ root.update((state) => ({ internal: { ...state.internal, active: false } }));
2081
+ try {
2082
+ const state = root.get();
2083
+ state.events.disconnect?.();
2084
+ state.gl?.renderLists?.dispose?.();
2085
+ state.gl?.forceContextLoss?.();
2086
+ if (state.gl?.xr)
2087
+ state.xr.disconnect();
2088
+ dispose(state);
2089
+ roots.delete(canvas);
2090
+ }
2091
+ catch (e) {
2092
+ console.error('[NGT] Unexpected error while destroying Canvas Root', e);
2093
+ }
2094
+ }
2095
+ },
2096
+ configure: (inputs) => {
2097
+ const { shadows = false, linear = false, flat = false, legacy = false, orthographic = false, frameloop = 'always', dpr = [1, 2], gl: glOptions, size: sizeOptions, camera: cameraOptions, raycaster: raycasterOptions, scene: sceneOptions, events, lookAt, performance, } = inputs;
2098
+ const state = store.snapshot;
2099
+ const stateToUpdate = {};
2100
+ // setup renderer
2101
+ let gl = state.gl;
2102
+ if (!state.gl)
2103
+ stateToUpdate.gl = gl = makeRendererInstance(glOptions, canvas);
2104
+ // setup raycaster
2105
+ let raycaster = state.raycaster;
2106
+ if (!raycaster)
2107
+ stateToUpdate.raycaster = raycaster = new Raycaster();
2108
+ // set raycaster options
2109
+ const { params, ...options } = raycasterOptions || {};
2110
+ if (!is.equ(options, raycaster, shallowLoose))
2111
+ applyProps(raycaster, options);
2112
+ if (!is.equ(params, raycaster.params, shallowLoose)) {
2113
+ applyProps(raycaster, { params: { ...raycaster.params, ...(params || {}) } });
2114
+ }
2115
+ // Create default camera, don't overwrite any user-set state
2116
+ if (!state.camera || (state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose))) {
2117
+ lastCamera = cameraOptions;
2118
+ const isCamera = is.camera(cameraOptions);
2119
+ let camera = isCamera ? cameraOptions : makeCameraInstance(orthographic, state.size);
2120
+ if (!isCamera) {
2121
+ camera.position.z = 5;
2122
+ if (cameraOptions) {
2123
+ applyProps(camera, cameraOptions);
2124
+ if ('aspect' in cameraOptions ||
2125
+ 'left' in cameraOptions ||
2126
+ 'right' in cameraOptions ||
2127
+ 'top' in cameraOptions ||
2128
+ 'bottom' in cameraOptions) {
2129
+ Object.assign(camera, { manual: true });
2130
+ camera?.updateProjectionMatrix();
2131
+ }
2132
+ }
2133
+ // always look at center or passed-in lookAt by default
2134
+ if (!state.camera && !cameraOptions?.rotation && !cameraOptions?.quaternion) {
2135
+ if (Array.isArray(lookAt))
2136
+ camera.lookAt(lookAt[0], lookAt[1], lookAt[2]);
2137
+ else if (lookAt instanceof Vector3)
2138
+ camera.lookAt(lookAt);
2139
+ else
2140
+ camera.lookAt(0, 0, 0);
2141
+ }
2142
+ // update projection matrix after applyprops
2143
+ camera.updateProjectionMatrix?.();
2144
+ }
2145
+ if (!is.instance(camera))
2146
+ camera = prepare(camera, { store });
2147
+ stateToUpdate.camera = camera;
2148
+ // Configure raycaster
2149
+ // https://github.com/pmndrs/react-xr/issues/300
2150
+ raycaster.camera = camera;
2151
+ }
2152
+ // Set up scene (one time only!)
2153
+ if (!state.scene) {
2154
+ let scene;
2155
+ if (sceneOptions instanceof Scene) {
2156
+ scene = sceneOptions;
2157
+ }
2158
+ else {
2159
+ scene = new Scene();
2160
+ if (sceneOptions)
2161
+ applyProps(scene, sceneOptions);
2162
+ }
2163
+ applyProps(scene, {
2164
+ setAttribute: (name, value) => {
2165
+ if (canvas instanceof HTMLCanvasElement) {
2166
+ if (canvas.parentElement) {
2167
+ canvas.parentElement.setAttribute(name, value);
2168
+ }
2169
+ else {
2170
+ canvas.setAttribute(name, value);
2171
+ }
2172
+ }
2173
+ },
2174
+ });
2175
+ stateToUpdate.scene = prepare(scene, { store });
2176
+ }
2177
+ // Set up XR (one time only!)
2178
+ if (!state.xr) {
2179
+ // Handle frame behavior in WebXR
2180
+ const handleXRFrame = (timestamp, frame) => {
2181
+ const state = store.snapshot;
2182
+ if (state.frameloop === 'never')
2183
+ return;
2184
+ loop.advance(timestamp, true, store, frame);
2185
+ };
2186
+ // Toggle render switching on session
2187
+ const handleSessionChange = () => {
2188
+ const state = store.snapshot;
2189
+ state.gl.xr.enabled = state.gl.xr.isPresenting;
2190
+ state.gl.xr.setAnimationLoop(state.gl.xr.isPresenting ? handleXRFrame : null);
2191
+ if (!state.gl.xr.isPresenting)
2192
+ loop.invalidate(store);
2193
+ };
2194
+ // WebXR session manager
2195
+ const xr = {
2196
+ connect: () => {
2197
+ gl.xr.addEventListener('sessionstart', handleSessionChange);
2198
+ gl.xr.addEventListener('sessionend', handleSessionChange);
2199
+ },
2200
+ disconnect: () => {
2201
+ gl.xr.removeEventListener('sessionstart', handleSessionChange);
2202
+ gl.xr.removeEventListener('sessionend', handleSessionChange);
2203
+ },
2204
+ };
2205
+ // Subscribe to WebXR session events
2206
+ if (gl.xr && typeof gl.xr.addEventListener === 'function')
2207
+ xr.connect();
2208
+ stateToUpdate.xr = xr;
2209
+ }
2210
+ // Set shadowmap
2211
+ if (gl.shadowMap) {
2212
+ const oldEnabled = gl.shadowMap.enabled;
2213
+ const oldType = gl.shadowMap.type;
2214
+ gl.shadowMap.enabled = !!shadows;
2215
+ if (typeof shadows === 'boolean') {
2216
+ gl.shadowMap.type = PCFSoftShadowMap;
2217
+ }
2218
+ else if (typeof shadows === 'string') {
2219
+ const types = {
2220
+ basic: BasicShadowMap,
2221
+ percentage: PCFShadowMap,
2222
+ soft: PCFSoftShadowMap,
2223
+ variance: VSMShadowMap,
2224
+ };
2225
+ gl.shadowMap.type = types[shadows] ?? PCFSoftShadowMap;
2226
+ }
2227
+ else if (is.obj(shadows)) {
2228
+ Object.assign(gl.shadowMap, shadows);
2229
+ }
2230
+ if (oldEnabled !== gl.shadowMap.enabled || oldType !== gl.shadowMap.type)
2231
+ checkNeedsUpdate(gl.shadowMap);
2232
+ }
2233
+ // Safely set color management if available.
2234
+ // Avoid accessing ColorManagement to play nice with older versions
2235
+ if (ColorManagement) {
2236
+ const colorManagement = ColorManagement;
2237
+ if ('enabled' in colorManagement)
2238
+ colorManagement['enabled'] = !legacy ?? false;
2239
+ else if ('legacyMode' in colorManagement)
2240
+ colorManagement['legacyMode'] = legacy ?? true;
2241
+ }
2242
+ if (!isConfigured) {
2243
+ // set color space and tonemapping preferences once
2244
+ const LinearEncoding = 3000;
2245
+ const sRGBEncoding = 3001;
2246
+ applyProps(gl, {
2247
+ outputEncoding: linear ? LinearEncoding : sRGBEncoding,
2248
+ toneMapping: flat ? NoToneMapping : ACESFilmicToneMapping,
2249
+ });
2250
+ }
2251
+ // Update color management state
2252
+ if (state.legacy !== legacy)
2253
+ stateToUpdate.legacy = legacy;
2254
+ if (state.linear !== linear)
2255
+ stateToUpdate.linear = linear;
2256
+ if (state.flat !== flat)
2257
+ stateToUpdate.flat = flat;
2258
+ // Set gl props
2259
+ gl.setClearAlpha(0);
2260
+ gl.setPixelRatio(makeDpr(state.viewport.dpr));
2261
+ gl.setSize(state.size.width, state.size.height);
2262
+ if (is.obj(glOptions) &&
2263
+ !(typeof glOptions === 'function') &&
2264
+ !is.renderer(glOptions) &&
2265
+ !is.equ(glOptions, gl, shallowLoose)) {
2266
+ applyProps(gl, glOptions);
2267
+ }
2268
+ // Store events internally
2269
+ if (events && !state.events.handlers)
2270
+ stateToUpdate.events = events(store);
2271
+ // Check performance
2272
+ if (performance && !is.equ(performance, state.performance, shallowLoose)) {
2273
+ stateToUpdate.performance = { ...state.performance, ...performance };
2274
+ }
2275
+ if (Object.keys(stateToUpdate).length) {
2276
+ store.update(stateToUpdate);
2277
+ }
2278
+ // Check size, allow it to take on container bounds initially
2279
+ const size = computeInitialSize(canvas, sizeOptions);
2280
+ if (!is.equ(size, state.size, shallowLoose)) {
2281
+ state.setSize(size.width, size.height, size.top, size.left);
2282
+ }
2283
+ // Check pixelratio
2284
+ if (dpr && state.viewport.dpr !== makeDpr(dpr))
2285
+ state.setDpr(dpr);
2286
+ // Check frameloop
2287
+ if (state.frameloop !== frameloop)
2288
+ state.setFrameloop(frameloop);
2289
+ isConfigured = true;
2290
+ },
2291
+ };
2292
+ };
2293
+ });
2294
+ }
2295
+ function computeInitialSize(canvas, defaultSize) {
2296
+ if (defaultSize) {
2297
+ return defaultSize;
2303
2298
  }
2304
- get data() {
2305
- return this.delegate.data;
2299
+ if (typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
2300
+ return canvas.parentElement.getBoundingClientRect();
2301
+ }
2302
+ if (typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas) {
2303
+ return { width: canvas.width, height: canvas.height, top: 0, left: 0 };
2306
2304
  }
2305
+ return { width: 0, height: 0, top: 0, left: 0 };
2307
2306
  }
2308
- function provideNgtRenderer(store) {
2309
- const providers = [
2310
- NgtRendererFactory,
2311
- { provide: RendererFactory2, useExisting: NgtRendererFactory },
2312
- provideStore(() => store),
2313
- ];
2314
- return makeEnvironmentProviders(providers);
2307
+ // Disposes an object and all its properties
2308
+ function dispose(obj) {
2309
+ if (obj.dispose && obj.type !== 'Scene')
2310
+ obj.dispose();
2311
+ for (const p in obj) {
2312
+ p.dispose?.();
2313
+ delete obj[p];
2314
+ }
2315
2315
  }
2316
2316
 
2317
2317
  var _a$1;
@@ -2514,6 +2514,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.1.2", ngImpor
2514
2514
  changeDetection: ChangeDetectionStrategy.OnPush,
2515
2515
  }]
2516
2516
  }], ctorParameters: () => [] });
2517
+ const injectRoot = injectCanvasRootInitializer;
2517
2518
 
2518
2519
  var _a;
2519
2520
  const NGT_HTML_DOM_ELEMENT = new InjectionToken('NGT_HTML_DOM_ELEMENT');
@@ -2971,5 +2972,5 @@ function vector3(inputOrOptions, keyOrKeepUndefined, keepUndefined) {
2971
2972
  * Generated bundle index. Do not edit.
2972
2973
  */
2973
2974
 
2974
- export { HTML, NGT_STORE, NgtArgs, NgtCanvas, NgtHTML, NgtPortal, NgtPortalContent, NgtRenderer, NgtRendererFactory, NgtRoutedScene, ROUTED_SCENE, addAfterEffect, addEffect, addTail, applyProps, checkNeedsUpdate, checkUpdate, createAttachFunction, extend, getLocalState, injectBeforeRender, injectCanvasRootInitializer, injectLoader, injectObjectEvents, injectStore, invalidateInstance, is, makeCameraInstance, makeDpr, makeId, makeObjectGraph, makeRendererInstance, merge, omit, pick, prepare, provideHTMLDomElement, provideNgtRenderer, provideStore, resolveRef, signalStore, updateCamera, vector2, vector3 };
2975
+ export { HTML, NGT_STORE, NgtArgs, NgtCanvas, NgtHTML, NgtPortal, NgtPortalBeforeRender, NgtPortalContent, NgtRenderer, NgtRendererFactory, NgtRoutedScene, ROUTED_SCENE, addAfterEffect, addEffect, addTail, applyProps, attach, checkNeedsUpdate, checkUpdate, createAttachFunction, detach, dispose, extend, flushGlobalEffects, getLocalState, injectBeforeRender, injectCanvasRootInitializer, injectLoader, injectLoop, injectObjectEvents, injectRoot, injectStore, invalidateInstance, is, makeCameraInstance, makeDpr, makeId, makeObjectGraph, makeRendererInstance, merge, omit, pick, prepare, privateKeys, provideHTMLDomElement, provideNgtRenderer, provideStore, resolveRef, roots, signalStore, updateCamera, vector2, vector3 };
2975
2976
  //# sourceMappingURL=angular-three.mjs.map