@react-three/fiber 9.0.0-alpha.1 → 9.0.0-alpha.3

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 (35) hide show
  1. package/CHANGELOG.md +962 -444
  2. package/dist/declarations/src/core/events.d.ts +91 -69
  3. package/dist/declarations/src/core/hooks.d.ts +53 -24
  4. package/dist/declarations/src/core/index.d.ts +15 -61
  5. package/dist/declarations/src/core/loop.d.ts +31 -13
  6. package/dist/declarations/src/core/reconciler.d.ts +43 -0
  7. package/dist/declarations/src/core/renderer.d.ts +85 -51
  8. package/dist/declarations/src/core/stages.d.ts +64 -59
  9. package/dist/declarations/src/core/store.d.ts +147 -110
  10. package/dist/declarations/src/core/utils.d.ts +128 -81
  11. package/dist/declarations/src/index.d.ts +6 -12
  12. package/dist/declarations/src/native/Canvas.d.ts +14 -8
  13. package/dist/declarations/src/native/events.d.ts +4 -4
  14. package/dist/declarations/src/native.d.ts +6 -10
  15. package/dist/declarations/src/three-types.d.ts +56 -331
  16. package/dist/declarations/src/web/Canvas.d.ts +24 -9
  17. package/dist/declarations/src/web/events.d.ts +4 -4
  18. package/dist/loop-0698c205.cjs.dev.js +2496 -0
  19. package/dist/loop-a0ef8208.cjs.prod.js +2496 -0
  20. package/dist/loop-b2aca207.esm.js +2434 -0
  21. package/dist/react-three-fiber.cjs.d.ts +1 -0
  22. package/dist/react-three-fiber.cjs.dev.js +139 -100
  23. package/dist/react-three-fiber.cjs.prod.js +139 -100
  24. package/dist/react-three-fiber.esm.js +104 -68
  25. package/native/dist/react-three-fiber-native.cjs.d.ts +1 -0
  26. package/native/dist/react-three-fiber-native.cjs.dev.js +287 -212
  27. package/native/dist/react-three-fiber-native.cjs.prod.js +287 -212
  28. package/native/dist/react-three-fiber-native.esm.js +251 -181
  29. package/native/package.json +5 -5
  30. package/package.json +18 -11
  31. package/readme.md +253 -202
  32. package/dist/declarations/src/native/polyfills.d.ts +0 -1
  33. package/dist/index-2e1b7052.cjs.prod.js +0 -2362
  34. package/dist/index-65e750e4.cjs.dev.js +0 -2362
  35. package/dist/index-a9c7a6cd.esm.js +0 -2304
@@ -0,0 +1,2496 @@
1
+ 'use strict';
2
+
3
+ var THREE = require('three');
4
+ var React = require('react');
5
+ var constants = require('react-reconciler/constants');
6
+ var zustand = require('zustand');
7
+ var itsFine = require('its-fine');
8
+ var _extends = require('@babel/runtime/helpers/extends');
9
+ var Reconciler = require('react-reconciler');
10
+ var scheduler = require('scheduler');
11
+ var suspendReact = require('suspend-react');
12
+
13
+ function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
14
+
15
+ function _interopNamespace(e) {
16
+ if (e && e.__esModule) return e;
17
+ var n = Object.create(null);
18
+ if (e) {
19
+ Object.keys(e).forEach(function (k) {
20
+ if (k !== 'default') {
21
+ var d = Object.getOwnPropertyDescriptor(e, k);
22
+ Object.defineProperty(n, k, d.get ? d : {
23
+ enumerable: true,
24
+ get: function () { return e[k]; }
25
+ });
26
+ }
27
+ });
28
+ }
29
+ n["default"] = e;
30
+ return Object.freeze(n);
31
+ }
32
+
33
+ var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
34
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
35
+ var Reconciler__default = /*#__PURE__*/_interopDefault(Reconciler);
36
+
37
+ var threeTypes = /*#__PURE__*/Object.freeze({
38
+ __proto__: null
39
+ });
40
+
41
+ // TODO: handle constructor overloads
42
+ // https://github.com/pmndrs/react-three-fiber/pull/2931
43
+ // https://github.com/microsoft/TypeScript/issues/37079
44
+
45
+ const catalogue = {};
46
+ let i = 0;
47
+ const extend = objects => {
48
+ if (typeof objects === 'function') {
49
+ const Component = `${i++}`;
50
+ catalogue[Component] = objects;
51
+
52
+ // Returns a component whose name will be inferred in devtools
53
+ // @ts-ignore
54
+ return /*#__PURE__*/React__namespace.forwardRef({
55
+ [objects.name]: (props, ref) => /*#__PURE__*/React__namespace.createElement(Component, _extends({}, props, {
56
+ ref: ref
57
+ }))
58
+ }[objects.name]);
59
+ } else {
60
+ return void Object.assign(catalogue, objects);
61
+ }
62
+ };
63
+ function createInstance(type, props, root) {
64
+ // Get target from catalogue
65
+ const name = `${type[0].toUpperCase()}${type.slice(1)}`;
66
+ const target = catalogue[name];
67
+
68
+ // Validate element target
69
+ if (type !== 'primitive' && !target) throw new Error(`R3F: ${name} is not part of the THREE namespace! Did you forget to extend? See: https://docs.pmnd.rs/react-three-fiber/api/objects#using-3rd-party-objects-declaratively`);
70
+
71
+ // Validate primitives
72
+ if (type === 'primitive' && !props.object) throw new Error(`R3F: Primitives without 'object' are invalid!`);
73
+
74
+ // Throw if an object or literal was passed for args
75
+ if (props.args !== undefined && !Array.isArray(props.args)) throw new Error('R3F: The args prop must be an array!');
76
+
77
+ // Create instance
78
+ const instance = prepare(props.object, root, type, props);
79
+ return instance;
80
+ }
81
+
82
+ // https://github.com/facebook/react/issues/20271
83
+ // This will make sure events and attach are only handled once when trees are complete
84
+ function handleContainerEffects(parent, child, beforeChild) {
85
+ // Bail if tree isn't mounted or parent is not a container.
86
+ // This ensures that the tree is finalized and React won't discard results to Suspense
87
+ const state = child.root.getState();
88
+ if (!parent.parent && parent.object !== state.scene) return;
89
+
90
+ // Create & link object on first run
91
+ if (!child.object) {
92
+ var _child$props$object, _child$props$args;
93
+ // Get target from catalogue
94
+ const name = `${child.type[0].toUpperCase()}${child.type.slice(1)}`;
95
+ const target = catalogue[name];
96
+
97
+ // Create object
98
+ child.object = (_child$props$object = child.props.object) != null ? _child$props$object : new target(...((_child$props$args = child.props.args) != null ? _child$props$args : []));
99
+ child.object.__r3f = child;
100
+
101
+ // Set initial props
102
+ applyProps(child.object, child.props);
103
+ }
104
+
105
+ // Append instance
106
+ if (child.props.attach) {
107
+ attach(parent, child);
108
+ } else if (isObject3D(child.object) && isObject3D(parent.object)) {
109
+ const childIndex = parent.object.children.indexOf(beforeChild == null ? void 0 : beforeChild.object);
110
+ if (beforeChild && childIndex !== -1) {
111
+ child.object.parent = parent.object;
112
+ parent.object.children.splice(childIndex, 0, child.object);
113
+ child.object.dispatchEvent({
114
+ type: 'added'
115
+ });
116
+ parent.object.dispatchEvent({
117
+ type: 'childadded',
118
+ child: child.object
119
+ });
120
+ } else {
121
+ parent.object.add(child.object);
122
+ }
123
+ }
124
+
125
+ // Link subtree
126
+ for (const childInstance of child.children) handleContainerEffects(child, childInstance);
127
+
128
+ // Tree was updated, request a frame
129
+ invalidateInstance(child);
130
+ }
131
+ function appendChild(parent, child) {
132
+ if (!child) return;
133
+
134
+ // Link instances
135
+ child.parent = parent;
136
+ parent.children.push(child);
137
+
138
+ // Attach tree once complete
139
+ handleContainerEffects(parent, child);
140
+ }
141
+ function insertBefore(parent, child, beforeChild) {
142
+ if (!child || !beforeChild) return;
143
+
144
+ // Link instances
145
+ child.parent = parent;
146
+ const childIndex = parent.children.indexOf(beforeChild);
147
+ if (childIndex !== -1) parent.children.splice(childIndex, 0, child);else parent.children.push(child);
148
+
149
+ // Attach tree once complete
150
+ handleContainerEffects(parent, child, beforeChild);
151
+ }
152
+ function removeChild(parent, child, dispose, recursive) {
153
+ if (!child) return;
154
+
155
+ // Unlink instances
156
+ child.parent = null;
157
+ if (recursive === undefined) {
158
+ const childIndex = parent.children.indexOf(child);
159
+ if (childIndex !== -1) parent.children.splice(childIndex, 1);
160
+ }
161
+
162
+ // Eagerly tear down tree
163
+ if (child.props.attach) {
164
+ detach(parent, child);
165
+ } else if (isObject3D(child.object) && isObject3D(parent.object)) {
166
+ parent.object.remove(child.object);
167
+ removeInteractivity(findInitialRoot(child), child.object);
168
+ }
169
+
170
+ // Allow objects to bail out of unmount disposal with dispose={null}
171
+ const shouldDispose = child.props.dispose !== null && dispose !== false;
172
+
173
+ // Recursively remove instance children
174
+ if (recursive !== false) {
175
+ for (const node of child.children) removeChild(child, node, shouldDispose, true);
176
+ child.children.length = 0;
177
+ }
178
+
179
+ // Unlink instance object
180
+ delete child.object.__r3f;
181
+
182
+ // Dispose object whenever the reconciler feels like it.
183
+ // Never dispose of primitives because their state may be kept outside of React!
184
+ // In order for an object to be able to dispose it
185
+ // - has a dispose method
186
+ // - cannot be a <primitive object={...} />
187
+ // - cannot be a THREE.Scene, because three has broken its own API
188
+ if (shouldDispose && child.type !== 'primitive' && child.object.type !== 'Scene') {
189
+ if (typeof child.object.dispose === 'function') {
190
+ const dispose = child.object.dispose.bind(child.object);
191
+ const handleDispose = () => {
192
+ try {
193
+ dispose();
194
+ } catch (e) {
195
+ // no-op
196
+ }
197
+ };
198
+
199
+ // In a testing environment, cleanup immediately
200
+ if (typeof IS_REACT_ACT_ENVIRONMENT !== 'undefined') handleDispose();
201
+ // Otherwise, using a real GPU so schedule cleanup to prevent stalls
202
+ else scheduler.unstable_scheduleCallback(scheduler.unstable_IdlePriority, handleDispose);
203
+ }
204
+ }
205
+
206
+ // Tree was updated, request a frame for top-level instance
207
+ if (dispose === undefined) invalidateInstance(child);
208
+ }
209
+ function setFiberInstance(fiber, instance) {
210
+ if (fiber !== null) {
211
+ fiber.stateNode = instance;
212
+ if (typeof fiber.ref === 'function') fiber.ref(instance.object);else if (fiber.ref) fiber.ref.current = instance.object;
213
+ }
214
+ }
215
+ function switchInstance(oldInstance, type, props, fiber) {
216
+ // Create a new instance
217
+ const newInstance = createInstance(type, props, oldInstance.root);
218
+
219
+ // Move children to new instance
220
+ for (const child of oldInstance.children) {
221
+ removeChild(oldInstance, child, false, false);
222
+ appendChild(newInstance, child);
223
+ }
224
+ oldInstance.children.length = 0;
225
+
226
+ // Link up new instance
227
+ const parent = oldInstance.parent;
228
+ if (parent) {
229
+ // Manually handle replace https://github.com/pmndrs/react-three-fiber/pull/2680
230
+
231
+ newInstance.autoRemovedBeforeAppend = !!newInstance.parent;
232
+ if (!oldInstance.autoRemovedBeforeAppend) removeChild(parent, oldInstance);
233
+ appendChild(parent, newInstance);
234
+
235
+ // if (!oldInstance.autoRemovedBeforeAppend) {
236
+ // insertBefore(parent, newInstance, oldInstance)
237
+ // removeChild(parent, oldInstance)
238
+ // } else {
239
+ // appendChild(parent, newInstance)
240
+ // }
241
+ }
242
+
243
+ // This evil hack switches the react-internal fiber instance
244
+ // https://github.com/facebook/react/issues/14983
245
+ // TODO: investigate scheduling key prop change instead of switchInstance entirely
246
+ // https://github.com/facebook/react/pull/15021#issuecomment-480185369
247
+ setFiberInstance(fiber, newInstance);
248
+ setFiberInstance(fiber.alternate, newInstance);
249
+
250
+ // Tree was updated, request a frame
251
+ invalidateInstance(newInstance);
252
+ return newInstance;
253
+ }
254
+
255
+ // Don't handle text instances, warn on undefined behavior
256
+ const handleTextInstance = () => console.warn('R3F: Text is not allowed in JSX! This could be stray whitespace or characters.');
257
+ const NO_CONTEXT = {};
258
+ let currentUpdatePriority = constants.NoEventPriority;
259
+
260
+ // Effectively removed to diff in commit phase
261
+ // https://github.com/facebook/react/pull/27409
262
+ function prepareUpdate(instance, _type, oldProps, newProps) {
263
+ var _newProps$args, _oldProps$args, _newProps$args2;
264
+ // Reconstruct primitives if object prop changes
265
+ if (instance.type === 'primitive' && oldProps.object !== newProps.object) return [true];
266
+
267
+ // Throw if an object or literal was passed for args
268
+ if (newProps.args !== undefined && !Array.isArray(newProps.args)) throw new Error('R3F: The args prop must be an array!');
269
+
270
+ // Reconstruct instance if args change
271
+ if (((_newProps$args = newProps.args) == null ? void 0 : _newProps$args.length) !== ((_oldProps$args = oldProps.args) == null ? void 0 : _oldProps$args.length)) return [true];
272
+ if ((_newProps$args2 = newProps.args) != null && _newProps$args2.some((value, index) => {
273
+ var _oldProps$args2;
274
+ return value !== ((_oldProps$args2 = oldProps.args) == null ? void 0 : _oldProps$args2[index]);
275
+ })) return [true];
276
+
277
+ // Create a diff-set, flag if there are any changes
278
+ const changedProps = diffProps(instance, newProps, true);
279
+ if (Object.keys(changedProps).length) return [false, changedProps];
280
+
281
+ // Otherwise do not touch the instance
282
+ return null;
283
+ }
284
+ const reconciler = Reconciler__default["default"]({
285
+ isPrimaryRenderer: false,
286
+ warnsIfNotActing: false,
287
+ supportsMutation: true,
288
+ supportsPersistence: false,
289
+ supportsHydration: false,
290
+ createInstance,
291
+ removeChild,
292
+ appendChild,
293
+ appendInitialChild: appendChild,
294
+ insertBefore,
295
+ appendChildToContainer(container, child) {
296
+ const scene = container.getState().scene.__r3f;
297
+ if (!child || !scene) return;
298
+ appendChild(scene, child);
299
+ },
300
+ removeChildFromContainer(container, child) {
301
+ const scene = container.getState().scene.__r3f;
302
+ if (!child || !scene) return;
303
+ removeChild(scene, child);
304
+ },
305
+ insertInContainerBefore(container, child, beforeChild) {
306
+ const scene = container.getState().scene.__r3f;
307
+ if (!child || !beforeChild || !scene) return;
308
+ insertBefore(scene, child, beforeChild);
309
+ },
310
+ getRootHostContext: () => NO_CONTEXT,
311
+ getChildHostContext: () => NO_CONTEXT,
312
+ // @ts-ignore prepareUpdate and updatePayload removed with React 19
313
+ commitUpdate(instance, type, oldProps, newProps, fiber) {
314
+ const diff = prepareUpdate(instance, type, oldProps, newProps);
315
+ if (diff === null) return;
316
+ const [reconstruct, changedProps] = diff;
317
+
318
+ // Reconstruct when args or <primitive object={...} have changes
319
+ if (reconstruct) return switchInstance(instance, type, newProps, fiber);
320
+
321
+ // Otherwise just overwrite props
322
+ Object.assign(instance.props, changedProps);
323
+ applyProps(instance.object, changedProps);
324
+ },
325
+ finalizeInitialChildren: () => false,
326
+ commitMount() {},
327
+ getPublicInstance: instance => instance == null ? void 0 : instance.object,
328
+ prepareForCommit: () => null,
329
+ preparePortalMount: container => prepare(container.getState().scene, container, '', {}),
330
+ resetAfterCommit: () => {},
331
+ shouldSetTextContent: () => false,
332
+ clearContainer: () => false,
333
+ hideInstance(instance) {
334
+ var _instance$parent;
335
+ if (instance.props.attach && (_instance$parent = instance.parent) != null && _instance$parent.object) {
336
+ detach(instance.parent, instance);
337
+ } else if (isObject3D(instance.object)) {
338
+ instance.object.visible = false;
339
+ }
340
+ instance.isHidden = true;
341
+ invalidateInstance(instance);
342
+ },
343
+ unhideInstance(instance) {
344
+ if (instance.isHidden) {
345
+ var _instance$parent2;
346
+ if (instance.props.attach && (_instance$parent2 = instance.parent) != null && _instance$parent2.object) {
347
+ attach(instance.parent, instance);
348
+ } else if (isObject3D(instance.object) && instance.props.visible !== false) {
349
+ instance.object.visible = true;
350
+ }
351
+ }
352
+ instance.isHidden = false;
353
+ invalidateInstance(instance);
354
+ },
355
+ createTextInstance: handleTextInstance,
356
+ hideTextInstance: handleTextInstance,
357
+ unhideTextInstance: handleTextInstance,
358
+ scheduleTimeout: typeof setTimeout === 'function' ? setTimeout : undefined,
359
+ cancelTimeout: typeof clearTimeout === 'function' ? clearTimeout : undefined,
360
+ noTimeout: -1,
361
+ getInstanceFromNode: () => null,
362
+ beforeActiveInstanceBlur() {},
363
+ afterActiveInstanceBlur() {},
364
+ detachDeletedInstance() {},
365
+ // @ts-ignore untyped react-experimental options inspired by react-art
366
+ // TODO: add shell types for these and upstream to DefinitelyTyped
367
+ // https://github.com/facebook/react/blob/main/packages/react-art/src/ReactFiberConfigART.js
368
+ shouldAttemptEagerTransition() {
369
+ return false;
370
+ },
371
+ requestPostPaintCallback() {},
372
+ maySuspendCommit() {
373
+ return false;
374
+ },
375
+ preloadInstance() {
376
+ return true; // true indicates already loaded
377
+ },
378
+ startSuspendingCommit() {},
379
+ suspendInstance() {},
380
+ waitForCommitToBeReady() {
381
+ return null;
382
+ },
383
+ NotPendingTransition: null,
384
+ setCurrentUpdatePriority(newPriority) {
385
+ currentUpdatePriority = newPriority;
386
+ },
387
+ getCurrentUpdatePriority() {
388
+ return currentUpdatePriority;
389
+ },
390
+ resolveUpdatePriority() {
391
+ var _window$event;
392
+ if (currentUpdatePriority !== constants.NoEventPriority) return currentUpdatePriority;
393
+ switch (typeof window !== 'undefined' && ((_window$event = window.event) == null ? void 0 : _window$event.type)) {
394
+ case 'click':
395
+ case 'contextmenu':
396
+ case 'dblclick':
397
+ case 'pointercancel':
398
+ case 'pointerdown':
399
+ case 'pointerup':
400
+ return constants.DiscreteEventPriority;
401
+ case 'pointermove':
402
+ case 'pointerout':
403
+ case 'pointerover':
404
+ case 'pointerenter':
405
+ case 'pointerleave':
406
+ case 'wheel':
407
+ return constants.ContinuousEventPriority;
408
+ default:
409
+ return constants.DefaultEventPriority;
410
+ }
411
+ }
412
+ });
413
+
414
+ var _window$document, _window$navigator;
415
+ /**
416
+ * Returns the instance's initial (outmost) root.
417
+ */
418
+ function findInitialRoot(instance) {
419
+ let root = instance.root;
420
+ // TODO: this needs testing https://github.com/pmndrs/react-three-fiber/commit/a4a31ed93c48d1e6dac91329bb5f2ca6a25e5f9c
421
+ // while (root.getState().previousRoot) root = root.getState().previousRoot!
422
+ return root;
423
+ }
424
+
425
+ /**
426
+ * Returns `true` with correct TS type inference if an object has a configurable color space (since r152).
427
+ */
428
+ const hasColorSpace = object => 'colorSpace' in object || 'outputColorSpace' in object;
429
+ /**
430
+ * The current THREE.ColorManagement instance, if present.
431
+ */
432
+ const getColorManagement = () => {
433
+ var _ColorManagement;
434
+ return (_ColorManagement = catalogue.ColorManagement) != null ? _ColorManagement : null;
435
+ };
436
+ /**
437
+ * Safely flush async effects when testing, simulating a legacy root.
438
+ */
439
+ const act = React__namespace.act;
440
+ const isOrthographicCamera = def => def && def.isOrthographicCamera;
441
+ const isRef = obj => obj && obj.hasOwnProperty('current');
442
+
443
+ /**
444
+ * An SSR-friendly useLayoutEffect.
445
+ *
446
+ * React currently throws a warning when using useLayoutEffect on the server.
447
+ * To get around it, we can conditionally useEffect on the server (no-op) and
448
+ * useLayoutEffect elsewhere.
449
+ *
450
+ * @see https://github.com/facebook/react/issues/14927
451
+ */
452
+ const useIsomorphicLayoutEffect = typeof window !== 'undefined' && ((_window$document = window.document) != null && _window$document.createElement || ((_window$navigator = window.navigator) == null ? void 0 : _window$navigator.product) === 'ReactNative') ? React__namespace.useLayoutEffect : React__namespace.useEffect;
453
+ function useMutableCallback(fn) {
454
+ const ref = React__namespace.useRef(fn);
455
+ useIsomorphicLayoutEffect(() => void (ref.current = fn), [fn]);
456
+ return ref;
457
+ }
458
+ /**
459
+ * Bridges renderer Context and StrictMode from a primary renderer.
460
+ */
461
+ function useBridge() {
462
+ const fiber = itsFine.useFiber();
463
+ const ContextBridge = itsFine.useContextBridge();
464
+ return React__namespace.useMemo(() => ({
465
+ children
466
+ }) => {
467
+ const strict = !!itsFine.traverseFiber(fiber, true, node => node.type === React__namespace.StrictMode);
468
+ const Root = strict ? React__namespace.StrictMode : React__namespace.Fragment;
469
+ return /*#__PURE__*/React__namespace.createElement(Root, null, /*#__PURE__*/React__namespace.createElement(ContextBridge, null, children));
470
+ }, [fiber, ContextBridge]);
471
+ }
472
+ function Block({
473
+ set
474
+ }) {
475
+ useIsomorphicLayoutEffect(() => {
476
+ set(new Promise(() => null));
477
+ return () => set(false);
478
+ }, [set]);
479
+ return null;
480
+ }
481
+ class ErrorBoundary extends React__namespace.Component {
482
+ constructor(...args) {
483
+ super(...args);
484
+ this.state = {
485
+ error: false
486
+ };
487
+ }
488
+ componentDidCatch(err) {
489
+ this.props.set(err);
490
+ }
491
+ render() {
492
+ return this.state.error ? null : this.props.children;
493
+ }
494
+ }
495
+ ErrorBoundary.getDerivedStateFromError = () => ({
496
+ error: true
497
+ });
498
+ function calculateDpr(dpr) {
499
+ var _window$devicePixelRa;
500
+ // Err on the side of progress by assuming 2x dpr if we can't detect it
501
+ // This will happen in workers where window is defined but dpr isn't.
502
+ const target = typeof window !== 'undefined' ? (_window$devicePixelRa = window.devicePixelRatio) != null ? _window$devicePixelRa : 2 : 1;
503
+ return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], target), dpr[1]) : dpr;
504
+ }
505
+
506
+ /**
507
+ * Returns instance root state
508
+ */
509
+ function getRootState(obj) {
510
+ var _r3f;
511
+ return (_r3f = obj.__r3f) == null ? void 0 : _r3f.root.getState();
512
+ }
513
+ // A collection of compare functions
514
+ const is = {
515
+ obj: a => a === Object(a) && !is.arr(a) && typeof a !== 'function',
516
+ fun: a => typeof a === 'function',
517
+ str: a => typeof a === 'string',
518
+ num: a => typeof a === 'number',
519
+ boo: a => typeof a === 'boolean',
520
+ und: a => a === void 0,
521
+ arr: a => Array.isArray(a),
522
+ equ(a, b, {
523
+ arrays = 'shallow',
524
+ objects = 'reference',
525
+ strict = true
526
+ } = {}) {
527
+ // Wrong type or one of the two undefined, doesn't match
528
+ if (typeof a !== typeof b || !!a !== !!b) return false;
529
+ // Atomic, just compare a against b
530
+ if (is.str(a) || is.num(a)) return a === b;
531
+ const isObj = is.obj(a);
532
+ if (isObj && objects === 'reference') return a === b;
533
+ const isArr = is.arr(a);
534
+ if (isArr && arrays === 'reference') return a === b;
535
+ // Array or Object, shallow compare first to see if it's a match
536
+ if ((isArr || isObj) && a === b) return true;
537
+ // Last resort, go through keys
538
+ let i;
539
+ // Check if a has all the keys of b
540
+ for (i in a) if (!(i in b)) return false;
541
+ // Check if values between keys match
542
+ if (isObj && arrays === 'shallow' && objects === 'shallow') {
543
+ for (i in strict ? b : a) if (!is.equ(a[i], b[i], {
544
+ strict,
545
+ objects: 'reference'
546
+ })) return false;
547
+ } else {
548
+ for (i in strict ? b : a) if (a[i] !== b[i]) return false;
549
+ }
550
+ // If i is undefined
551
+ if (is.und(i)) {
552
+ // If both arrays are empty we consider them equal
553
+ if (isArr && a.length === 0 && b.length === 0) return true;
554
+ // If both objects are empty we consider them equal
555
+ if (isObj && Object.keys(a).length === 0 && Object.keys(b).length === 0) return true;
556
+ // Otherwise match them by value
557
+ if (a !== b) return false;
558
+ }
559
+ return true;
560
+ }
561
+ };
562
+
563
+ // Collects nodes and materials from a THREE.Object3D
564
+ function buildGraph(object) {
565
+ const data = {
566
+ nodes: {},
567
+ materials: {}
568
+ };
569
+ if (object) {
570
+ object.traverse(obj => {
571
+ if (obj.name) data.nodes[obj.name] = obj;
572
+ if (obj.material && !data.materials[obj.material.name]) data.materials[obj.material.name] = obj.material;
573
+ });
574
+ }
575
+ return data;
576
+ }
577
+ // Disposes an object and all its properties
578
+ function dispose(obj) {
579
+ if (obj.type !== 'Scene') obj.dispose == null ? void 0 : obj.dispose();
580
+ for (const p in obj) {
581
+ const prop = obj[p];
582
+ if ((prop == null ? void 0 : prop.type) !== 'Scene') prop == null ? void 0 : prop.dispose == null ? void 0 : prop.dispose();
583
+ }
584
+ }
585
+ const REACT_INTERNAL_PROPS = ['children', 'key', 'ref'];
586
+
587
+ // Gets only instance props from reconciler fibers
588
+ function getInstanceProps(queue) {
589
+ const props = {};
590
+ for (const key in queue) {
591
+ if (!REACT_INTERNAL_PROPS.includes(key)) props[key] = queue[key];
592
+ }
593
+ return props;
594
+ }
595
+
596
+ // Each object in the scene carries a small LocalState descriptor
597
+ function prepare(target, root, type, props) {
598
+ const object = target;
599
+
600
+ // Create instance descriptor
601
+ let instance = object == null ? void 0 : object.__r3f;
602
+ if (!instance) {
603
+ instance = {
604
+ root,
605
+ type,
606
+ parent: null,
607
+ children: [],
608
+ props: getInstanceProps(props),
609
+ object,
610
+ eventCount: 0,
611
+ handlers: {},
612
+ isHidden: false
613
+ };
614
+ if (object) {
615
+ object.__r3f = instance;
616
+ if (type) applyProps(object, instance.props);
617
+ }
618
+ }
619
+ return instance;
620
+ }
621
+ function resolve(root, key) {
622
+ var _target;
623
+ let target = root[key];
624
+ if (!key.includes('-')) return {
625
+ root,
626
+ key,
627
+ target
628
+ };
629
+
630
+ // Resolve pierced target
631
+ const chain = key.split('-');
632
+ target = chain.reduce((acc, key) => acc[key], root);
633
+ key = chain.pop();
634
+
635
+ // Switch root if atomic
636
+ if (!((_target = target) != null && _target.set)) root = chain.reduce((acc, key) => acc[key], root);
637
+ return {
638
+ root,
639
+ key,
640
+ target
641
+ };
642
+ }
643
+
644
+ // Checks if a dash-cased string ends with an integer
645
+ const INDEX_REGEX = /-\d+$/;
646
+ function attach(parent, child) {
647
+ if (is.str(child.props.attach)) {
648
+ // If attaching into an array (foo-0), create one
649
+ if (INDEX_REGEX.test(child.props.attach)) {
650
+ const index = child.props.attach.replace(INDEX_REGEX, '');
651
+ const {
652
+ root,
653
+ key
654
+ } = resolve(parent.object, index);
655
+ if (!Array.isArray(root[key])) root[key] = [];
656
+ }
657
+ const {
658
+ root,
659
+ key
660
+ } = resolve(parent.object, child.props.attach);
661
+ child.previousAttach = root[key];
662
+ root[key] = child.object;
663
+ } else if (is.fun(child.props.attach)) {
664
+ child.previousAttach = child.props.attach(parent.object, child.object);
665
+ }
666
+ }
667
+ function detach(parent, child) {
668
+ if (is.str(child.props.attach)) {
669
+ const {
670
+ root,
671
+ key
672
+ } = resolve(parent.object, child.props.attach);
673
+ const previous = child.previousAttach;
674
+ // When the previous value was undefined, it means the value was never set to begin with
675
+ if (previous === undefined) delete root[key];
676
+ // Otherwise set the previous value
677
+ else root[key] = previous;
678
+ } else {
679
+ child.previousAttach == null ? void 0 : child.previousAttach(parent.object, child.object);
680
+ }
681
+ delete child.previousAttach;
682
+ }
683
+ const RESERVED_PROPS = [...REACT_INTERNAL_PROPS,
684
+ // Instance props
685
+ 'args', 'dispose', 'attach', 'object',
686
+ // Behavior flags
687
+ 'dispose'];
688
+ const MEMOIZED_PROTOTYPES = new Map();
689
+
690
+ // This function prepares a set of changes to be applied to the instance
691
+ function diffProps(instance, newProps, resetRemoved = false) {
692
+ const changedProps = {};
693
+
694
+ // Sort through props
695
+ for (const prop in newProps) {
696
+ // Skip reserved keys
697
+ if (RESERVED_PROPS.includes(prop)) continue;
698
+ // Skip if props match
699
+ if (is.equ(newProps[prop], instance.props[prop])) continue;
700
+
701
+ // Props changed, add them
702
+ changedProps[prop] = newProps[prop];
703
+
704
+ // Reset pierced props
705
+ for (const other in newProps) {
706
+ if (other.startsWith(`${prop}-`)) changedProps[other] = newProps[other];
707
+ }
708
+ }
709
+
710
+ // Reset removed props for HMR
711
+ if (resetRemoved) {
712
+ for (const prop in instance.props) {
713
+ if (RESERVED_PROPS.includes(prop) || newProps.hasOwnProperty(prop)) continue;
714
+ const {
715
+ root,
716
+ key
717
+ } = resolve(instance.object, prop);
718
+
719
+ // https://github.com/mrdoob/three.js/issues/21209
720
+ // HMR/fast-refresh relies on the ability to cancel out props, but threejs
721
+ // has no means to do this. Hence we curate a small collection of value-classes
722
+ // with their respective constructor/set arguments
723
+ // For removed props, try to set default values, if possible
724
+ if (root.constructor && root.constructor.length === 0) {
725
+ // create a blank slate of the instance and copy the particular parameter.
726
+ let ctor = MEMOIZED_PROTOTYPES.get(root.constructor);
727
+ if (!ctor) {
728
+ ctor = new root.constructor();
729
+ MEMOIZED_PROTOTYPES.set(root.constructor, ctor);
730
+ }
731
+ changedProps[key] = ctor[key];
732
+ } else {
733
+ // instance does not have constructor, just set it to 0
734
+ changedProps[key] = 0;
735
+ }
736
+ }
737
+ }
738
+ return changedProps;
739
+ }
740
+ const __DEV__ = typeof process !== 'undefined' && process.env.NODE_ENV !== 'production';
741
+
742
+ // const LinearEncoding = 3000
743
+ const sRGBEncoding = 3001;
744
+ const SRGBColorSpace = 'srgb';
745
+ const LinearSRGBColorSpace = 'srgb-linear';
746
+
747
+ // https://github.com/mrdoob/three.js/pull/27042
748
+ // https://github.com/mrdoob/three.js/pull/22748
749
+ const colorMaps = ['map', 'emissiveMap', 'sheenTintMap',
750
+ // <r134
751
+ 'sheenColorMap', 'specularTintMap',
752
+ // <r134
753
+ 'specularColorMap', 'envMap'];
754
+ const EVENT_REGEX = /^on(Pointer|Click|DoubleClick|ContextMenu|Wheel)/;
755
+
756
+ // This function applies a set of changes to the instance
757
+ function applyProps(object, props) {
758
+ const instance = object.__r3f;
759
+ const rootState = instance && findInitialRoot(instance).getState();
760
+ const prevHandlers = instance == null ? void 0 : instance.eventCount;
761
+ for (const prop in props) {
762
+ let value = props[prop];
763
+
764
+ // Don't mutate reserved keys
765
+ if (RESERVED_PROPS.includes(prop)) continue;
766
+
767
+ // Deal with pointer events, including removing them if undefined
768
+ if (instance && EVENT_REGEX.test(prop)) {
769
+ if (typeof value === 'function') instance.handlers[prop] = value;else delete instance.handlers[prop];
770
+ instance.eventCount = Object.keys(instance.handlers).length;
771
+ }
772
+
773
+ // Ignore setting undefined props
774
+ // https://github.com/pmndrs/react-three-fiber/issues/274
775
+ if (value === undefined) continue;
776
+ let {
777
+ root,
778
+ key,
779
+ target
780
+ } = resolve(object, prop);
781
+
782
+ // Alias (output)encoding => (output)colorSpace (since r152)
783
+ // https://github.com/pmndrs/react-three-fiber/pull/2829
784
+ if (hasColorSpace(root)) {
785
+ if (key === 'encoding') {
786
+ key = 'colorSpace';
787
+ value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
788
+ } else if (key === 'outputEncoding') {
789
+ key = 'outputColorSpace';
790
+ value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
791
+ }
792
+ }
793
+
794
+ // Copy if properties match signatures
795
+ if (target != null && target.copy && (
796
+ // Some environments may break strict identity checks by duplicating versions of three.js.
797
+ // Loosen to unminified names, ignoring descendents.
798
+ // https://github.com/pmndrs/react-three-fiber/issues/2856
799
+ // TODO: fix upstream and remove in v9
800
+ __DEV__ ? target.constructor.name === value.constructor.name : target.constructor === value.constructor)) {
801
+ target.copy(value);
802
+ }
803
+ // Layers have no copy function, we must therefore copy the mask property
804
+ else if (target instanceof THREE__namespace.Layers && value instanceof THREE__namespace.Layers) {
805
+ target.mask = value.mask;
806
+ }
807
+ // Set array types
808
+ else if (target != null && target.set && Array.isArray(value)) {
809
+ if (target.fromArray) target.fromArray(value);else target.set(...value);
810
+ }
811
+ // Set literal types
812
+ else if (target != null && target.set && typeof value !== 'object') {
813
+ const isColor = target instanceof THREE__namespace.Color;
814
+ // Allow setting array scalars
815
+ if (!isColor && target.setScalar && typeof value === 'number') target.setScalar(value);
816
+ // Otherwise just set single value
817
+ else target.set(value);
818
+
819
+ // Emulate THREE.ColorManagement for older three.js versions
820
+ // https://github.com/pmndrs/react-three-fiber/issues/344
821
+ if (!getColorManagement() && !(rootState != null && rootState.linear) && isColor) target.convertSRGBToLinear();
822
+ }
823
+ // Else, just overwrite the value
824
+ else {
825
+ root[key] = value;
826
+
827
+ // Auto-convert sRGB texture parameters for built-in materials
828
+ // https://github.com/pmndrs/react-three-fiber/issues/344
829
+ // https://github.com/mrdoob/three.js/pull/25857
830
+ if (rootState && !rootState.linear && colorMaps.includes(key) && root[key] instanceof THREE__namespace.Texture &&
831
+ // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
832
+ root[key].format === THREE__namespace.RGBAFormat && root[key].type === THREE__namespace.UnsignedByteType) {
833
+ // NOTE: this cannot be set from the renderer (e.g. sRGB source textures rendered to P3)
834
+ if (hasColorSpace(root[key])) root[key].colorSpace = 'srgb';else root[key].encoding = sRGBEncoding;
835
+ }
836
+ }
837
+ }
838
+
839
+ // Register event handlers
840
+ if (instance != null && instance.parent && rootState != null && rootState.internal && instance.object instanceof THREE__namespace.Object3D && prevHandlers !== instance.eventCount) {
841
+ // Pre-emptively remove the instance from the interaction manager
842
+ const index = rootState.internal.interaction.indexOf(instance.object);
843
+ if (index > -1) rootState.internal.interaction.splice(index, 1);
844
+ // Add the instance to the interaction manager only when it has handlers
845
+ if (instance.eventCount && instance.object.raycast !== null && instance.object instanceof THREE__namespace.Object3D) {
846
+ rootState.internal.interaction.push(instance.object);
847
+ }
848
+ }
849
+
850
+ // Auto-attach geometries and materials
851
+ if (instance && instance.props.attach === undefined) {
852
+ if (instance.object instanceof THREE__namespace.BufferGeometry) instance.props.attach = 'geometry';else if (instance.object instanceof THREE__namespace.Material) instance.props.attach = 'material';
853
+ }
854
+
855
+ // Instance was updated, request a frame
856
+ if (instance) invalidateInstance(instance);
857
+ return object;
858
+ }
859
+ function invalidateInstance(instance) {
860
+ var _instance$root;
861
+ const state = (_instance$root = instance.root) == null ? void 0 : _instance$root.getState == null ? void 0 : _instance$root.getState();
862
+ if (state && state.internal.frames === 0) state.invalidate();
863
+ }
864
+ function updateCamera(camera, size) {
865
+ // Do not mess with the camera if it belongs to the user
866
+ // https://github.com/pmndrs/react-three-fiber/issues/92
867
+ if (camera.manual) return;
868
+ if (isOrthographicCamera(camera)) {
869
+ camera.left = size.width / -2;
870
+ camera.right = size.width / 2;
871
+ camera.top = size.height / 2;
872
+ camera.bottom = size.height / -2;
873
+ } else {
874
+ camera.aspect = size.width / size.height;
875
+ }
876
+ camera.updateProjectionMatrix();
877
+ }
878
+ const isObject3D = object => object == null ? void 0 : object.isObject3D;
879
+
880
+ function makeId(event) {
881
+ return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
882
+ }
883
+
884
+ /**
885
+ * Release pointer captures.
886
+ * This is called by releasePointerCapture in the API, and when an object is removed.
887
+ */
888
+ function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
889
+ const captureData = captures.get(obj);
890
+ if (captureData) {
891
+ captures.delete(obj);
892
+ // If this was the last capturing object for this pointer
893
+ if (captures.size === 0) {
894
+ capturedMap.delete(pointerId);
895
+ captureData.target.releasePointerCapture(pointerId);
896
+ }
897
+ }
898
+ }
899
+ function removeInteractivity(store, object) {
900
+ const {
901
+ internal
902
+ } = store.getState();
903
+ // Removes every trace of an object from the data store
904
+ internal.interaction = internal.interaction.filter(o => o !== object);
905
+ internal.initialHits = internal.initialHits.filter(o => o !== object);
906
+ internal.hovered.forEach((value, key) => {
907
+ if (value.eventObject === object || value.object === object) {
908
+ // Clear out intersects, they are outdated by now
909
+ internal.hovered.delete(key);
910
+ }
911
+ });
912
+ internal.capturedMap.forEach((captures, pointerId) => {
913
+ releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
914
+ });
915
+ }
916
+ function createEvents(store) {
917
+ /** Calculates delta */
918
+ function calculateDistance(event) {
919
+ const {
920
+ internal
921
+ } = store.getState();
922
+ const dx = event.offsetX - internal.initialClick[0];
923
+ const dy = event.offsetY - internal.initialClick[1];
924
+ return Math.round(Math.sqrt(dx * dx + dy * dy));
925
+ }
926
+
927
+ /** Returns true if an instance has a valid pointer-event registered, this excludes scroll, clicks etc */
928
+ function filterPointerEvents(objects) {
929
+ return objects.filter(obj => ['Move', 'Over', 'Enter', 'Out', 'Leave'].some(name => {
930
+ var _r3f;
931
+ return (_r3f = obj.__r3f) == null ? void 0 : _r3f.handlers['onPointer' + name];
932
+ }));
933
+ }
934
+ function intersect(event, filter) {
935
+ const state = store.getState();
936
+ const duplicates = new Set();
937
+ const intersections = [];
938
+ // Allow callers to eliminate event objects
939
+ const eventsObjects = filter ? filter(state.internal.interaction) : state.internal.interaction;
940
+ // Reset all raycaster cameras to undefined
941
+ for (let i = 0; i < eventsObjects.length; i++) {
942
+ const state = getRootState(eventsObjects[i]);
943
+ if (state) {
944
+ state.raycaster.camera = undefined;
945
+ }
946
+ }
947
+ if (!state.previousRoot) {
948
+ // Make sure root-level pointer and ray are set up
949
+ state.events.compute == null ? void 0 : state.events.compute(event, state);
950
+ }
951
+ function handleRaycast(obj) {
952
+ const state = getRootState(obj);
953
+ // Skip event handling when noEvents is set, or when the raycasters camera is null
954
+ if (!state || !state.events.enabled || state.raycaster.camera === null) return [];
955
+
956
+ // When the camera is undefined we have to call the event layers update function
957
+ if (state.raycaster.camera === undefined) {
958
+ var _state$previousRoot;
959
+ state.events.compute == null ? void 0 : state.events.compute(event, state, (_state$previousRoot = state.previousRoot) == null ? void 0 : _state$previousRoot.getState());
960
+ // If the camera is still undefined we have to skip this layer entirely
961
+ if (state.raycaster.camera === undefined) state.raycaster.camera = null;
962
+ }
963
+
964
+ // Intersect object by object
965
+ return state.raycaster.camera ? state.raycaster.intersectObject(obj, true) : [];
966
+ }
967
+
968
+ // Collect events
969
+ let hits = eventsObjects
970
+ // Intersect objects
971
+ .flatMap(handleRaycast)
972
+ // Sort by event priority and distance
973
+ .sort((a, b) => {
974
+ const aState = getRootState(a.object);
975
+ const bState = getRootState(b.object);
976
+ if (!aState || !bState) return a.distance - b.distance;
977
+ return bState.events.priority - aState.events.priority || a.distance - b.distance;
978
+ })
979
+ // Filter out duplicates
980
+ .filter(item => {
981
+ const id = makeId(item);
982
+ if (duplicates.has(id)) return false;
983
+ duplicates.add(id);
984
+ return true;
985
+ });
986
+
987
+ // https://github.com/mrdoob/three.js/issues/16031
988
+ // Allow custom userland intersect sort order, this likely only makes sense on the root filter
989
+ if (state.events.filter) hits = state.events.filter(hits, state);
990
+
991
+ // Bubble up the events, find the event source (eventObject)
992
+ for (const hit of hits) {
993
+ let eventObject = hit.object;
994
+ // Bubble event up
995
+ while (eventObject) {
996
+ var _r3f2;
997
+ if ((_r3f2 = eventObject.__r3f) != null && _r3f2.eventCount) intersections.push({
998
+ ...hit,
999
+ eventObject
1000
+ });
1001
+ eventObject = eventObject.parent;
1002
+ }
1003
+ }
1004
+
1005
+ // If the interaction is captured, make all capturing targets part of the intersect.
1006
+ if ('pointerId' in event && state.internal.capturedMap.has(event.pointerId)) {
1007
+ for (let captureData of state.internal.capturedMap.get(event.pointerId).values()) {
1008
+ if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
1009
+ }
1010
+ }
1011
+ return intersections;
1012
+ }
1013
+
1014
+ /** Handles intersections by forwarding them to handlers */
1015
+ function handleIntersects(intersections, event, delta, callback) {
1016
+ // If anything has been found, forward it to the event listeners
1017
+ if (intersections.length) {
1018
+ const localState = {
1019
+ stopped: false
1020
+ };
1021
+ for (const hit of intersections) {
1022
+ const state = getRootState(hit.object);
1023
+ if (state) {
1024
+ const {
1025
+ raycaster,
1026
+ pointer,
1027
+ camera,
1028
+ internal
1029
+ } = state;
1030
+ const unprojectedPoint = new THREE__namespace.Vector3(pointer.x, pointer.y, 0).unproject(camera);
1031
+ const hasPointerCapture = id => {
1032
+ var _internal$capturedMap, _internal$capturedMap2;
1033
+ return (_internal$capturedMap = (_internal$capturedMap2 = internal.capturedMap.get(id)) == null ? void 0 : _internal$capturedMap2.has(hit.eventObject)) != null ? _internal$capturedMap : false;
1034
+ };
1035
+ const setPointerCapture = id => {
1036
+ const captureData = {
1037
+ intersection: hit,
1038
+ target: event.target
1039
+ };
1040
+ if (internal.capturedMap.has(id)) {
1041
+ // if the pointerId was previously captured, we add the hit to the
1042
+ // event capturedMap.
1043
+ internal.capturedMap.get(id).set(hit.eventObject, captureData);
1044
+ } else {
1045
+ // if the pointerId was not previously captured, we create a map
1046
+ // containing the hitObject, and the hit. hitObject is used for
1047
+ // faster access.
1048
+ internal.capturedMap.set(id, new Map([[hit.eventObject, captureData]]));
1049
+ }
1050
+ event.target.setPointerCapture(id);
1051
+ };
1052
+ const releasePointerCapture = id => {
1053
+ const captures = internal.capturedMap.get(id);
1054
+ if (captures) {
1055
+ releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
1056
+ }
1057
+ };
1058
+
1059
+ // Add native event props
1060
+ let extractEventProps = {};
1061
+ // This iterates over the event's properties including the inherited ones. Native PointerEvents have most of their props as getters which are inherited, but polyfilled PointerEvents have them all as their own properties (i.e. not inherited). We can't use Object.keys() or Object.entries() as they only return "own" properties; nor Object.getPrototypeOf(event) as that *doesn't* return "own" properties, only inherited ones.
1062
+ for (let prop in event) {
1063
+ let property = event[prop];
1064
+ // Only copy over atomics, leave functions alone as these should be
1065
+ // called as event.nativeEvent.fn()
1066
+ if (typeof property !== 'function') extractEventProps[prop] = property;
1067
+ }
1068
+ let raycastEvent = {
1069
+ ...hit,
1070
+ ...extractEventProps,
1071
+ pointer,
1072
+ intersections,
1073
+ stopped: localState.stopped,
1074
+ delta,
1075
+ unprojectedPoint,
1076
+ ray: raycaster.ray,
1077
+ camera: camera,
1078
+ // Hijack stopPropagation, which just sets a flag
1079
+ stopPropagation() {
1080
+ // https://github.com/pmndrs/react-three-fiber/issues/596
1081
+ // Events are not allowed to stop propagation if the pointer has been captured
1082
+ const capturesForPointer = 'pointerId' in event && internal.capturedMap.get(event.pointerId);
1083
+
1084
+ // We only authorize stopPropagation...
1085
+ if (
1086
+ // ...if this pointer hasn't been captured
1087
+ !capturesForPointer ||
1088
+ // ... or if the hit object is capturing the pointer
1089
+ capturesForPointer.has(hit.eventObject)) {
1090
+ raycastEvent.stopped = localState.stopped = true;
1091
+ // Propagation is stopped, remove all other hover records
1092
+ // An event handler is only allowed to flush other handlers if it is hovered itself
1093
+ if (internal.hovered.size && Array.from(internal.hovered.values()).find(i => i.eventObject === hit.eventObject)) {
1094
+ // Objects cannot flush out higher up objects that have already caught the event
1095
+ const higher = intersections.slice(0, intersections.indexOf(hit));
1096
+ cancelPointer([...higher, hit]);
1097
+ }
1098
+ }
1099
+ },
1100
+ // there should be a distinction between target and currentTarget
1101
+ target: {
1102
+ hasPointerCapture,
1103
+ setPointerCapture,
1104
+ releasePointerCapture
1105
+ },
1106
+ currentTarget: {
1107
+ hasPointerCapture,
1108
+ setPointerCapture,
1109
+ releasePointerCapture
1110
+ },
1111
+ nativeEvent: event
1112
+ };
1113
+
1114
+ // Call subscribers
1115
+ callback(raycastEvent);
1116
+ // Event bubbling may be interrupted by stopPropagation
1117
+ if (localState.stopped === true) break;
1118
+ }
1119
+ }
1120
+ }
1121
+ return intersections;
1122
+ }
1123
+ function cancelPointer(intersections) {
1124
+ const {
1125
+ internal
1126
+ } = store.getState();
1127
+ for (const hoveredObj of internal.hovered.values()) {
1128
+ // When no objects were hit or the the hovered object wasn't found underneath the cursor
1129
+ // we call onPointerOut and delete the object from the hovered-elements map
1130
+ if (!intersections.length || !intersections.find(hit => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId)) {
1131
+ const eventObject = hoveredObj.eventObject;
1132
+ const instance = eventObject.__r3f;
1133
+ internal.hovered.delete(makeId(hoveredObj));
1134
+ if (instance != null && instance.eventCount) {
1135
+ const handlers = instance.handlers;
1136
+ // Clear out intersects, they are outdated by now
1137
+ const data = {
1138
+ ...hoveredObj,
1139
+ intersections
1140
+ };
1141
+ handlers.onPointerOut == null ? void 0 : handlers.onPointerOut(data);
1142
+ handlers.onPointerLeave == null ? void 0 : handlers.onPointerLeave(data);
1143
+ }
1144
+ }
1145
+ }
1146
+ }
1147
+ function pointerMissed(event, objects) {
1148
+ for (let i = 0; i < objects.length; i++) {
1149
+ const instance = objects[i].__r3f;
1150
+ instance == null ? void 0 : instance.handlers.onPointerMissed == null ? void 0 : instance.handlers.onPointerMissed(event);
1151
+ }
1152
+ }
1153
+ function handlePointer(name) {
1154
+ // Deal with cancelation
1155
+ switch (name) {
1156
+ case 'onPointerLeave':
1157
+ case 'onPointerCancel':
1158
+ return () => cancelPointer([]);
1159
+ case 'onLostPointerCapture':
1160
+ return event => {
1161
+ const {
1162
+ internal
1163
+ } = store.getState();
1164
+ if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {
1165
+ // If the object event interface had onLostPointerCapture, we'd call it here on every
1166
+ // object that's getting removed. We call it on the next frame because onLostPointerCapture
1167
+ // fires before onPointerUp. Otherwise pointerUp would never be called if the event didn't
1168
+ // happen in the object it originated from, leaving components in a in-between state.
1169
+ requestAnimationFrame(() => {
1170
+ // Only release if pointer-up didn't do it already
1171
+ if (internal.capturedMap.has(event.pointerId)) {
1172
+ internal.capturedMap.delete(event.pointerId);
1173
+ cancelPointer([]);
1174
+ }
1175
+ });
1176
+ }
1177
+ };
1178
+ }
1179
+
1180
+ // Any other pointer goes here ...
1181
+ return function handleEvent(event) {
1182
+ const {
1183
+ onPointerMissed,
1184
+ internal
1185
+ } = store.getState();
1186
+
1187
+ // prepareRay(event)
1188
+ internal.lastEvent.current = event;
1189
+
1190
+ // Get fresh intersects
1191
+ const isPointerMove = name === 'onPointerMove';
1192
+ const isClickEvent = name === 'onClick' || name === 'onContextMenu' || name === 'onDoubleClick';
1193
+ const filter = isPointerMove ? filterPointerEvents : undefined;
1194
+ const hits = intersect(event, filter);
1195
+ const delta = isClickEvent ? calculateDistance(event) : 0;
1196
+
1197
+ // Save initial coordinates on pointer-down
1198
+ if (name === 'onPointerDown') {
1199
+ internal.initialClick = [event.offsetX, event.offsetY];
1200
+ internal.initialHits = hits.map(hit => hit.eventObject);
1201
+ }
1202
+
1203
+ // If a click yields no results, pass it back to the user as a miss
1204
+ // Missed events have to come first in order to establish user-land side-effect clean up
1205
+ if (isClickEvent && !hits.length) {
1206
+ if (delta <= 2) {
1207
+ pointerMissed(event, internal.interaction);
1208
+ if (onPointerMissed) onPointerMissed(event);
1209
+ }
1210
+ }
1211
+ // Take care of unhover
1212
+ if (isPointerMove) cancelPointer(hits);
1213
+ function onIntersect(data) {
1214
+ const eventObject = data.eventObject;
1215
+ const instance = eventObject.__r3f;
1216
+
1217
+ // Check presence of handlers
1218
+ if (!(instance != null && instance.eventCount)) return;
1219
+ const handlers = instance.handlers;
1220
+
1221
+ /*
1222
+ MAYBE TODO, DELETE IF NOT:
1223
+ Check if the object is captured, captured events should not have intersects running in parallel
1224
+ But wouldn't it be better to just replace capturedMap with a single entry?
1225
+ Also, are we OK with straight up making picking up multiple objects impossible?
1226
+
1227
+ const pointerId = (data as ThreeEvent<PointerEvent>).pointerId
1228
+ if (pointerId !== undefined) {
1229
+ const capturedMeshSet = internal.capturedMap.get(pointerId)
1230
+ if (capturedMeshSet) {
1231
+ const captured = capturedMeshSet.get(eventObject)
1232
+ if (captured && captured.localState.stopped) return
1233
+ }
1234
+ }*/
1235
+
1236
+ if (isPointerMove) {
1237
+ // Move event ...
1238
+ if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
1239
+ // When enter or out is present take care of hover-state
1240
+ const id = makeId(data);
1241
+ const hoveredItem = internal.hovered.get(id);
1242
+ if (!hoveredItem) {
1243
+ // If the object wasn't previously hovered, book it and call its handler
1244
+ internal.hovered.set(id, data);
1245
+ handlers.onPointerOver == null ? void 0 : handlers.onPointerOver(data);
1246
+ handlers.onPointerEnter == null ? void 0 : handlers.onPointerEnter(data);
1247
+ } else if (hoveredItem.stopped) {
1248
+ // If the object was previously hovered and stopped, we shouldn't allow other items to proceed
1249
+ data.stopPropagation();
1250
+ }
1251
+ }
1252
+ // Call mouse move
1253
+ handlers.onPointerMove == null ? void 0 : handlers.onPointerMove(data);
1254
+ } else {
1255
+ // All other events ...
1256
+ const handler = handlers[name];
1257
+ if (handler) {
1258
+ // Forward all events back to their respective handlers with the exception of click events,
1259
+ // which must use the initial target
1260
+ if (!isClickEvent || internal.initialHits.includes(eventObject)) {
1261
+ // Missed events have to come first
1262
+ pointerMissed(event, internal.interaction.filter(object => !internal.initialHits.includes(object)));
1263
+ // Now call the handler
1264
+ handler(data);
1265
+ }
1266
+ } else {
1267
+ // Trigger onPointerMissed on all elements that have pointer over/out handlers, but not click and weren't hit
1268
+ if (isClickEvent && internal.initialHits.includes(eventObject)) {
1269
+ pointerMissed(event, internal.interaction.filter(object => !internal.initialHits.includes(object)));
1270
+ }
1271
+ }
1272
+ }
1273
+ }
1274
+ handleIntersects(hits, event, delta, onIntersect);
1275
+ };
1276
+ }
1277
+ return {
1278
+ handlePointer
1279
+ };
1280
+ }
1281
+
1282
+ const isRenderer = def => !!(def != null && def.render);
1283
+ const context = /*#__PURE__*/React__namespace.createContext(null);
1284
+ const createStore = (invalidate, advance) => {
1285
+ const rootStore = zustand.create((set, get) => {
1286
+ const position = new THREE__namespace.Vector3();
1287
+ const defaultTarget = new THREE__namespace.Vector3();
1288
+ const tempTarget = new THREE__namespace.Vector3();
1289
+ function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
1290
+ const {
1291
+ width,
1292
+ height,
1293
+ top,
1294
+ left
1295
+ } = size;
1296
+ const aspect = width / height;
1297
+ if (target instanceof THREE__namespace.Vector3) tempTarget.copy(target);else tempTarget.set(...target);
1298
+ const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
1299
+ if (isOrthographicCamera(camera)) {
1300
+ return {
1301
+ width: width / camera.zoom,
1302
+ height: height / camera.zoom,
1303
+ top,
1304
+ left,
1305
+ factor: 1,
1306
+ distance,
1307
+ aspect
1308
+ };
1309
+ } else {
1310
+ const fov = camera.fov * Math.PI / 180; // convert vertical fov to radians
1311
+ const h = 2 * Math.tan(fov / 2) * distance; // visible height
1312
+ const w = h * (width / height);
1313
+ return {
1314
+ width: w,
1315
+ height: h,
1316
+ top,
1317
+ left,
1318
+ factor: width / w,
1319
+ distance,
1320
+ aspect
1321
+ };
1322
+ }
1323
+ }
1324
+ let performanceTimeout = undefined;
1325
+ const setPerformanceCurrent = current => set(state => ({
1326
+ performance: {
1327
+ ...state.performance,
1328
+ current
1329
+ }
1330
+ }));
1331
+ const pointer = new THREE__namespace.Vector2();
1332
+ const rootState = {
1333
+ set,
1334
+ get,
1335
+ // Mock objects that have to be configured
1336
+ gl: null,
1337
+ camera: null,
1338
+ raycaster: null,
1339
+ events: {
1340
+ priority: 1,
1341
+ enabled: true,
1342
+ connected: false
1343
+ },
1344
+ scene: null,
1345
+ xr: null,
1346
+ invalidate: (frames = 1) => invalidate(get(), frames),
1347
+ advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
1348
+ legacy: false,
1349
+ linear: false,
1350
+ flat: false,
1351
+ controls: null,
1352
+ clock: new THREE__namespace.Clock(),
1353
+ pointer,
1354
+ mouse: pointer,
1355
+ frameloop: 'always',
1356
+ onPointerMissed: undefined,
1357
+ performance: {
1358
+ current: 1,
1359
+ min: 0.5,
1360
+ max: 1,
1361
+ debounce: 200,
1362
+ regress: () => {
1363
+ const state = get();
1364
+ // Clear timeout
1365
+ if (performanceTimeout) clearTimeout(performanceTimeout);
1366
+ // Set lower bound performance
1367
+ if (state.performance.current !== state.performance.min) setPerformanceCurrent(state.performance.min);
1368
+ // Go back to upper bound performance after a while unless something regresses meanwhile
1369
+ performanceTimeout = setTimeout(() => setPerformanceCurrent(get().performance.max), state.performance.debounce);
1370
+ }
1371
+ },
1372
+ size: {
1373
+ width: 0,
1374
+ height: 0,
1375
+ top: 0,
1376
+ left: 0
1377
+ },
1378
+ viewport: {
1379
+ initialDpr: 0,
1380
+ dpr: 0,
1381
+ width: 0,
1382
+ height: 0,
1383
+ top: 0,
1384
+ left: 0,
1385
+ aspect: 0,
1386
+ distance: 0,
1387
+ factor: 0,
1388
+ getCurrentViewport
1389
+ },
1390
+ setEvents: events => set(state => ({
1391
+ ...state,
1392
+ events: {
1393
+ ...state.events,
1394
+ ...events
1395
+ }
1396
+ })),
1397
+ setSize: (width, height, top = 0, left = 0) => {
1398
+ const camera = get().camera;
1399
+ const size = {
1400
+ width,
1401
+ height,
1402
+ top,
1403
+ left
1404
+ };
1405
+ set(state => ({
1406
+ size,
1407
+ viewport: {
1408
+ ...state.viewport,
1409
+ ...getCurrentViewport(camera, defaultTarget, size)
1410
+ }
1411
+ }));
1412
+ },
1413
+ setDpr: dpr => set(state => {
1414
+ const resolved = calculateDpr(dpr);
1415
+ return {
1416
+ viewport: {
1417
+ ...state.viewport,
1418
+ dpr: resolved,
1419
+ initialDpr: state.viewport.initialDpr || resolved
1420
+ }
1421
+ };
1422
+ }),
1423
+ setFrameloop: frameloop => {
1424
+ var _frameloop$mode, _frameloop$render, _frameloop$maxDelta;
1425
+ const state = get();
1426
+ const mode = typeof frameloop === 'string' ? frameloop : (frameloop == null ? void 0 : frameloop.mode) === 'auto' ? 'always' : (_frameloop$mode = frameloop == null ? void 0 : frameloop.mode) != null ? _frameloop$mode : state.frameloop;
1427
+ const render = typeof frameloop === 'string' ? state.internal.render : (_frameloop$render = frameloop == null ? void 0 : frameloop.render) != null ? _frameloop$render : state.internal.render;
1428
+ const maxDelta = typeof frameloop === 'string' ? state.internal.maxDelta : (_frameloop$maxDelta = frameloop == null ? void 0 : frameloop.maxDelta) != null ? _frameloop$maxDelta : state.internal.maxDelta;
1429
+ const clock = state.clock;
1430
+ // if frameloop === "never" clock.elapsedTime is updated using advance(timestamp)
1431
+ clock.stop();
1432
+ clock.elapsedTime = 0;
1433
+ if (frameloop !== 'never') {
1434
+ clock.start();
1435
+ clock.elapsedTime = 0;
1436
+ }
1437
+ set(() => ({
1438
+ frameloop: mode,
1439
+ internal: {
1440
+ ...state.internal,
1441
+ render,
1442
+ maxDelta
1443
+ }
1444
+ }));
1445
+ },
1446
+ previousRoot: undefined,
1447
+ internal: {
1448
+ // Events
1449
+ interaction: [],
1450
+ hovered: new Map(),
1451
+ subscribers: [],
1452
+ initialClick: [0, 0],
1453
+ initialHits: [],
1454
+ capturedMap: new Map(),
1455
+ lastEvent: /*#__PURE__*/React__namespace.createRef(),
1456
+ // Updates
1457
+ active: false,
1458
+ frames: 0,
1459
+ stages: [],
1460
+ render: 'auto',
1461
+ maxDelta: 1 / 10,
1462
+ priority: 0,
1463
+ subscribe: (ref, priority, store) => {
1464
+ const state = get();
1465
+ const internal = state.internal;
1466
+ // If this subscription was given a priority, it takes rendering into its own hands
1467
+ // For that reason we switch off automatic rendering and increase the manual flag
1468
+ // As long as this flag is positive there can be no internal rendering at all
1469
+ // because there could be multiple render subscriptions
1470
+ internal.priority = internal.priority + (priority > 0 ? 1 : 0);
1471
+ // We use the render flag and deprecate priority
1472
+ if (internal.priority && state.internal.render === 'auto') set(() => ({
1473
+ internal: {
1474
+ ...state.internal,
1475
+ render: 'manual'
1476
+ }
1477
+ }));
1478
+ internal.subscribers.push({
1479
+ ref,
1480
+ priority,
1481
+ store
1482
+ });
1483
+ // Register subscriber and sort layers from lowest to highest, meaning,
1484
+ // highest priority renders last (on top of the other frames)
1485
+ internal.subscribers = internal.subscribers.sort((a, b) => a.priority - b.priority);
1486
+ return () => {
1487
+ const state = get();
1488
+ const internal = state.internal;
1489
+ if (internal != null && internal.subscribers) {
1490
+ // Decrease manual flag if this subscription had a priority
1491
+ internal.priority = internal.priority - (priority > 0 ? 1 : 0);
1492
+ // We use the render flag and deprecate priority
1493
+ if (!internal.priority && state.internal.render === 'manual') set(() => ({
1494
+ internal: {
1495
+ ...state.internal,
1496
+ render: 'auto'
1497
+ }
1498
+ }));
1499
+ // Remove subscriber from list
1500
+ internal.subscribers = internal.subscribers.filter(s => s.ref !== ref);
1501
+ }
1502
+ };
1503
+ }
1504
+ }
1505
+ };
1506
+ return rootState;
1507
+ });
1508
+ const state = rootStore.getState();
1509
+ let oldSize = state.size;
1510
+ let oldDpr = state.viewport.dpr;
1511
+ let oldCamera = state.camera;
1512
+ rootStore.subscribe(() => {
1513
+ const {
1514
+ camera,
1515
+ size,
1516
+ viewport,
1517
+ gl,
1518
+ set
1519
+ } = rootStore.getState();
1520
+
1521
+ // Resize camera and renderer on changes to size and pixelratio
1522
+ if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
1523
+ oldSize = size;
1524
+ oldDpr = viewport.dpr;
1525
+ // Update camera & renderer
1526
+ updateCamera(camera, size);
1527
+ gl.setPixelRatio(viewport.dpr);
1528
+ const updateStyle = typeof HTMLCanvasElement !== 'undefined' && gl.domElement instanceof HTMLCanvasElement;
1529
+ gl.setSize(size.width, size.height, updateStyle);
1530
+ }
1531
+
1532
+ // Update viewport once the camera changes
1533
+ if (camera !== oldCamera) {
1534
+ oldCamera = camera;
1535
+ // Update viewport
1536
+ set(state => ({
1537
+ viewport: {
1538
+ ...state.viewport,
1539
+ ...state.viewport.getCurrentViewport(camera)
1540
+ }
1541
+ }));
1542
+ }
1543
+ });
1544
+
1545
+ // Invalidate on any change
1546
+ rootStore.subscribe(state => invalidate(state));
1547
+
1548
+ // Return root state
1549
+ return rootStore;
1550
+ };
1551
+
1552
+ // TODO: Remove deprecated fields in `Subscription`
1553
+
1554
+ /**
1555
+ * Class representing a stage that updates every frame.
1556
+ * Stages are used to build a lifecycle of effects for an app's frameloop.
1557
+ */
1558
+ class Stage {
1559
+ constructor() {
1560
+ this.subscribers = [];
1561
+ this._frameTime = 0;
1562
+ }
1563
+
1564
+ /**
1565
+ * Executes all callback subscriptions on the stage.
1566
+ * @param delta - Delta time between frame calls.
1567
+ * @param [frame] - The XR frame if it exists.
1568
+ */
1569
+ frame(delta, frame) {
1570
+ const subs = this.subscribers;
1571
+ const initialTime = performance.now();
1572
+ for (let i = 0; i < subs.length; i++) {
1573
+ subs[i].ref.current(subs[i].store.getState(), delta, frame);
1574
+ }
1575
+ this._frameTime = performance.now() - initialTime;
1576
+ }
1577
+
1578
+ /**
1579
+ * Adds a callback subscriber to the stage.
1580
+ * @param ref - The mutable callback reference.
1581
+ * @param store - The store to be used with the callback execution.
1582
+ * @returns A function to remove the subscription.
1583
+ */
1584
+ add(ref, store) {
1585
+ this.subscribers.push({
1586
+ ref,
1587
+ store
1588
+ });
1589
+ return () => {
1590
+ this.subscribers = this.subscribers.filter(sub => {
1591
+ return sub.ref !== ref;
1592
+ });
1593
+ };
1594
+ }
1595
+ get frameTime() {
1596
+ return this._frameTime;
1597
+ }
1598
+ }
1599
+
1600
+ // Using Unity's fixedStep default.
1601
+ const FPS_50 = 1 / 50;
1602
+
1603
+ /**
1604
+ * Class representing a stage that updates every frame at a fixed rate.
1605
+ * @param name - Name of the stage.
1606
+ * @param [fixedStep] - Fixed step rate.
1607
+ * @param [maxSubsteps] - Maximum number of substeps.
1608
+ */
1609
+ class FixedStage extends Stage {
1610
+ constructor(fixedStep, maxSubSteps) {
1611
+ super();
1612
+ this._fixedStep = fixedStep != null ? fixedStep : FPS_50;
1613
+ this._maxSubsteps = maxSubSteps != null ? maxSubSteps : 6;
1614
+ this._accumulator = 0;
1615
+ this._alpha = 0;
1616
+ this._fixedFrameTime = 0;
1617
+ this._substepTimes = [];
1618
+ }
1619
+
1620
+ /**
1621
+ * Executes all callback subscriptions on the stage.
1622
+ * @param delta - Delta time between frame calls.
1623
+ * @param [frame] - The XR frame if it exists.
1624
+ */
1625
+ frame(delta, frame) {
1626
+ const initialTime = performance.now();
1627
+ let substeps = 0;
1628
+ this._substepTimes = [];
1629
+ this._accumulator += delta;
1630
+ while (this._accumulator >= this._fixedStep && substeps < this._maxSubsteps) {
1631
+ this._accumulator -= this._fixedStep;
1632
+ substeps++;
1633
+ super.frame(this._fixedStep, frame);
1634
+ this._substepTimes.push(super.frameTime);
1635
+ }
1636
+ this._fixedFrameTime = performance.now() - initialTime;
1637
+
1638
+ // The accumulator will only be larger than the fixed step if we had to
1639
+ // bail early due to hitting the max substep limit or execution time lagging.
1640
+ // In that case, we want to shave off the excess so we don't fall behind next frame.
1641
+ this._accumulator = this._accumulator % this._fixedStep;
1642
+ this._alpha = this._accumulator / this._fixedStep;
1643
+ }
1644
+ get frameTime() {
1645
+ return this._fixedFrameTime;
1646
+ }
1647
+ get substepTimes() {
1648
+ return this._substepTimes;
1649
+ }
1650
+ get fixedStep() {
1651
+ return this._fixedStep;
1652
+ }
1653
+ set fixedStep(fixedStep) {
1654
+ this._fixedStep = fixedStep;
1655
+ }
1656
+ get maxSubsteps() {
1657
+ return this._maxSubsteps;
1658
+ }
1659
+ set maxSubsteps(maxSubsteps) {
1660
+ this._maxSubsteps = maxSubsteps;
1661
+ }
1662
+ get accumulator() {
1663
+ return this._accumulator;
1664
+ }
1665
+ get alpha() {
1666
+ return this._alpha;
1667
+ }
1668
+ }
1669
+ const Early = /*#__PURE__*/new Stage();
1670
+ const Fixed = /*#__PURE__*/new FixedStage();
1671
+ const Update = /*#__PURE__*/new Stage();
1672
+ const Late = /*#__PURE__*/new Stage();
1673
+ const Render = /*#__PURE__*/new Stage();
1674
+ const After = /*#__PURE__*/new Stage();
1675
+ const Stages = {
1676
+ Early,
1677
+ Fixed,
1678
+ Update,
1679
+ Late,
1680
+ Render,
1681
+ After
1682
+ };
1683
+ const Lifecycle = [Early, Fixed, Update, Late, Render, After];
1684
+
1685
+ /**
1686
+ * Exposes an object's {@link Instance}.
1687
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#useInstanceHandle
1688
+ *
1689
+ * **Note**: this is an escape hatch to react-internal fields. Expect this to change significantly between versions.
1690
+ */
1691
+ function useInstanceHandle(ref) {
1692
+ const instance = React__namespace.useRef(null);
1693
+ useIsomorphicLayoutEffect(() => void (instance.current = ref.current.__r3f), [ref]);
1694
+ return instance;
1695
+ }
1696
+ function useStore() {
1697
+ const store = React__namespace.useContext(context);
1698
+ if (!store) throw new Error('R3F: Hooks can only be used within the Canvas component!');
1699
+ return store;
1700
+ }
1701
+
1702
+ /**
1703
+ * Accesses R3F's internal state, containing renderer, canvas, scene, etc.
1704
+ * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usethree
1705
+ */
1706
+ function useThree(selector = state => state, equalityFn) {
1707
+ // TODO: fix this type
1708
+ return useStore()(selector, equalityFn);
1709
+ }
1710
+
1711
+ /**
1712
+ * Executes a callback before render in a shared frame loop.
1713
+ * Can order effects with render priority or manually render with a positive priority.
1714
+ * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe
1715
+ */
1716
+ function useFrame(callback, renderPriority = 0) {
1717
+ const store = useStore();
1718
+ const subscribe = store.getState().internal.subscribe;
1719
+ // Memoize ref
1720
+ const ref = useMutableCallback(callback);
1721
+ // Subscribe on mount, unsubscribe on unmount
1722
+ useIsomorphicLayoutEffect(() => subscribe(ref, renderPriority, store), [renderPriority, subscribe, store]);
1723
+ return null;
1724
+ }
1725
+
1726
+ /**
1727
+ * Executes a callback in a given update stage.
1728
+ * Uses the stage instance to indetify which stage to target in the lifecycle.
1729
+ */
1730
+ function useUpdate(callback, stage = Stages.Update) {
1731
+ const store = useStore();
1732
+ const stages = store.getState().internal.stages;
1733
+ // Memoize ref
1734
+ const ref = useMutableCallback(callback);
1735
+ // Throw an error if a stage does not exist in the lifecycle
1736
+ if (!stages.includes(stage)) throw new Error(`An invoked stage does not exist in the lifecycle.`);
1737
+ // Subscribe on mount, unsubscribe on unmount
1738
+ useIsomorphicLayoutEffect(() => stage.add(ref, store), [stage]);
1739
+ }
1740
+
1741
+ /**
1742
+ * Returns a node graph of an object with named nodes & materials.
1743
+ * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usegraph
1744
+ */
1745
+ function useGraph(object) {
1746
+ return React__namespace.useMemo(() => buildGraph(object), [object]);
1747
+ }
1748
+ const memoizedLoaders = new WeakMap();
1749
+ const isConstructor = value => {
1750
+ var _value$prototype;
1751
+ return typeof value === 'function' && (value == null ? void 0 : (_value$prototype = value.prototype) == null ? void 0 : _value$prototype.constructor) === value;
1752
+ };
1753
+ function loadingFn(extensions, onProgress) {
1754
+ return async function (Proto, ...input) {
1755
+ let loader;
1756
+
1757
+ // Construct and cache loader if constructor was passed
1758
+ if (isConstructor(Proto)) {
1759
+ loader = memoizedLoaders.get(Proto);
1760
+ if (!loader) {
1761
+ loader = new Proto();
1762
+ memoizedLoaders.set(Proto, loader);
1763
+ }
1764
+ } else {
1765
+ loader = Proto;
1766
+ }
1767
+
1768
+ // Apply loader extensions
1769
+ if (extensions) extensions(loader);
1770
+
1771
+ // Go through the urls and load them
1772
+ return Promise.all(input.map(input => new Promise((res, reject) => loader.load(input, data => res(isObject3D(data == null ? void 0 : data.scene) ? Object.assign(data, buildGraph(data.scene)) : data), onProgress, error => reject(new Error(`Could not load ${input}: ${error == null ? void 0 : error.message}`))))));
1773
+ };
1774
+ }
1775
+ /**
1776
+ * Synchronously loads and caches assets with a three loader.
1777
+ *
1778
+ * Note: this hook's caller must be wrapped with `React.Suspense`
1779
+ * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useloader
1780
+ */
1781
+ function useLoader(loader, input, extensions, onProgress) {
1782
+ // Use suspense to load async assets
1783
+ const keys = Array.isArray(input) ? input : [input];
1784
+ const results = suspendReact.suspend(loadingFn(extensions, onProgress), [loader, ...keys], {
1785
+ equal: is.equ
1786
+ });
1787
+ // Return the object(s)
1788
+ return Array.isArray(input) ? results : results[0];
1789
+ }
1790
+
1791
+ /**
1792
+ * Preloads an asset into cache as a side-effect.
1793
+ */
1794
+ useLoader.preload = function (loader, input, extensions) {
1795
+ const keys = Array.isArray(input) ? input : [input];
1796
+ return suspendReact.preload(loadingFn(extensions), [loader, ...keys]);
1797
+ };
1798
+
1799
+ /**
1800
+ * Removes a loaded asset from cache.
1801
+ */
1802
+ useLoader.clear = function (loader, input) {
1803
+ const keys = Array.isArray(input) ? input : [input];
1804
+ return suspendReact.clear([loader, ...keys]);
1805
+ };
1806
+
1807
+ // TODO: fix type resolve
1808
+
1809
+ const _roots = new Map();
1810
+ const shallowLoose = {
1811
+ objects: 'shallow',
1812
+ strict: false
1813
+ };
1814
+ const createRendererInstance = (gl, canvas) => {
1815
+ const customRenderer = typeof gl === 'function' ? gl(canvas) : gl;
1816
+ if (isRenderer(customRenderer)) return customRenderer;
1817
+ return new THREE__namespace.WebGLRenderer({
1818
+ powerPreference: 'high-performance',
1819
+ canvas: canvas,
1820
+ antialias: true,
1821
+ alpha: true,
1822
+ ...gl
1823
+ });
1824
+ };
1825
+ const createStages = (stages, store) => {
1826
+ const state = store.getState();
1827
+ let subscribers;
1828
+ let subscription;
1829
+ const _stages = stages != null ? stages : Lifecycle;
1830
+ if (!_stages.includes(Stages.Update)) throw 'The Stages.Update stage is required for R3F.';
1831
+ if (!_stages.includes(Stages.Render)) throw 'The Stages.Render stage is required for R3F.';
1832
+ state.set(({
1833
+ internal
1834
+ }) => ({
1835
+ internal: {
1836
+ ...internal,
1837
+ stages: _stages
1838
+ }
1839
+ }));
1840
+
1841
+ // Add useFrame loop to update stage
1842
+ const frameCallback = {
1843
+ current(state, delta, frame) {
1844
+ subscribers = state.internal.subscribers;
1845
+ for (let i = 0; i < subscribers.length; i++) {
1846
+ subscription = subscribers[i];
1847
+ subscription.ref.current(subscription.store.getState(), delta, frame);
1848
+ }
1849
+ }
1850
+ };
1851
+ Stages.Update.add(frameCallback, store);
1852
+
1853
+ // Add render callback to render stage
1854
+ const renderCallback = {
1855
+ current(state) {
1856
+ if (state.internal.render === 'auto' && state.gl.render) state.gl.render(state.scene, state.camera);
1857
+ }
1858
+ };
1859
+ Stages.Render.add(renderCallback, store);
1860
+ };
1861
+ function computeInitialSize(canvas, size) {
1862
+ if (!size && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
1863
+ const {
1864
+ width,
1865
+ height,
1866
+ top,
1867
+ left
1868
+ } = canvas.parentElement.getBoundingClientRect();
1869
+ return {
1870
+ width,
1871
+ height,
1872
+ top,
1873
+ left
1874
+ };
1875
+ } else if (!size && typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas) {
1876
+ return {
1877
+ width: canvas.width,
1878
+ height: canvas.height,
1879
+ top: 0,
1880
+ left: 0
1881
+ };
1882
+ }
1883
+ return {
1884
+ width: 0,
1885
+ height: 0,
1886
+ top: 0,
1887
+ left: 0,
1888
+ ...size
1889
+ };
1890
+ }
1891
+ function createRoot(canvas) {
1892
+ // Check against mistaken use of createRoot
1893
+ const prevRoot = _roots.get(canvas);
1894
+ const prevFiber = prevRoot == null ? void 0 : prevRoot.fiber;
1895
+ const prevStore = prevRoot == null ? void 0 : prevRoot.store;
1896
+ if (prevRoot) console.warn('R3F.createRoot should only be called once!');
1897
+
1898
+ // Report when an error was detected in a previous render
1899
+ // https://github.com/pmndrs/react-three-fiber/pull/2261
1900
+ const logRecoverableError = typeof reportError === 'function' ?
1901
+ // In modern browsers, reportError will dispatch an error event,
1902
+ // emulating an uncaught JavaScript error.
1903
+ reportError :
1904
+ // In older browsers and test environments, fallback to console.error.
1905
+ console.error;
1906
+
1907
+ // Create store
1908
+ const store = prevStore || createStore(invalidate, advance);
1909
+ // Create renderer
1910
+ const fiber = prevFiber || reconciler.createContainer(store,
1911
+ // container
1912
+ constants.ConcurrentRoot,
1913
+ // tag
1914
+ null,
1915
+ // hydration callbacks
1916
+ false,
1917
+ // isStrictMode
1918
+ null,
1919
+ // concurrentUpdatesByDefaultOverride
1920
+ '',
1921
+ // identifierPrefix
1922
+ logRecoverableError,
1923
+ // onUncaughtError
1924
+ logRecoverableError,
1925
+ // onCaughtError
1926
+ logRecoverableError,
1927
+ // onRecoverableError
1928
+ null // transitionCallbacks
1929
+ );
1930
+ // Map it
1931
+ if (!prevRoot) _roots.set(canvas, {
1932
+ fiber,
1933
+ store
1934
+ });
1935
+
1936
+ // Locals
1937
+ let onCreated;
1938
+ let configured = false;
1939
+ let lastCamera;
1940
+ return {
1941
+ configure(props = {}) {
1942
+ let {
1943
+ gl: glConfig,
1944
+ size: propsSize,
1945
+ scene: sceneOptions,
1946
+ events,
1947
+ onCreated: onCreatedCallback,
1948
+ shadows = false,
1949
+ linear = false,
1950
+ flat = false,
1951
+ legacy = false,
1952
+ orthographic = false,
1953
+ frameloop = 'always',
1954
+ dpr = [1, 2],
1955
+ performance,
1956
+ raycaster: raycastOptions,
1957
+ camera: cameraOptions,
1958
+ onPointerMissed,
1959
+ stages
1960
+ } = props;
1961
+ let state = store.getState();
1962
+
1963
+ // Set up renderer (one time only!)
1964
+ let gl = state.gl;
1965
+ if (!state.gl) state.set({
1966
+ gl: gl = createRendererInstance(glConfig, canvas)
1967
+ });
1968
+
1969
+ // Set up raycaster (one time only!)
1970
+ let raycaster = state.raycaster;
1971
+ if (!raycaster) state.set({
1972
+ raycaster: raycaster = new THREE__namespace.Raycaster()
1973
+ });
1974
+
1975
+ // Set raycaster options
1976
+ const {
1977
+ params,
1978
+ ...options
1979
+ } = raycastOptions || {};
1980
+ if (!is.equ(options, raycaster, shallowLoose)) applyProps(raycaster, {
1981
+ ...options
1982
+ });
1983
+ if (!is.equ(params, raycaster.params, shallowLoose)) applyProps(raycaster, {
1984
+ params: {
1985
+ ...raycaster.params,
1986
+ ...params
1987
+ }
1988
+ });
1989
+
1990
+ // Create default camera, don't overwrite any user-set state
1991
+ if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
1992
+ lastCamera = cameraOptions;
1993
+ const isCamera = cameraOptions instanceof THREE__namespace.Camera;
1994
+ const camera = isCamera ? cameraOptions : orthographic ? new THREE__namespace.OrthographicCamera(0, 0, 0, 0, 0.1, 1000) : new THREE__namespace.PerspectiveCamera(75, 0, 0.1, 1000);
1995
+ if (!isCamera) {
1996
+ camera.position.z = 5;
1997
+ if (cameraOptions) applyProps(camera, cameraOptions);
1998
+ // Always look at center by default
1999
+ if (!state.camera && !(cameraOptions != null && cameraOptions.rotation)) camera.lookAt(0, 0, 0);
2000
+ }
2001
+ state.set({
2002
+ camera
2003
+ });
2004
+
2005
+ // Configure raycaster
2006
+ // https://github.com/pmndrs/react-xr/issues/300
2007
+ raycaster.camera = camera;
2008
+ }
2009
+
2010
+ // Set up scene (one time only!)
2011
+ if (!state.scene) {
2012
+ let scene;
2013
+ if (sceneOptions instanceof THREE__namespace.Scene) {
2014
+ scene = sceneOptions;
2015
+ prepare(scene, store, '', {});
2016
+ } else {
2017
+ scene = new THREE__namespace.Scene();
2018
+ prepare(scene, store, '', {});
2019
+ if (sceneOptions) applyProps(scene, sceneOptions);
2020
+ }
2021
+ state.set({
2022
+ scene
2023
+ });
2024
+ }
2025
+
2026
+ // Set up XR (one time only!)
2027
+ if (!state.xr) {
2028
+ // Handle frame behavior in WebXR
2029
+ const handleXRFrame = (timestamp, frame) => {
2030
+ const state = store.getState();
2031
+ if (state.frameloop === 'never') return;
2032
+ advance(timestamp, true, state, frame);
2033
+ };
2034
+
2035
+ // Toggle render switching on session
2036
+ const handleSessionChange = () => {
2037
+ const state = store.getState();
2038
+ state.gl.xr.enabled = state.gl.xr.isPresenting;
2039
+ state.gl.xr.setAnimationLoop(state.gl.xr.isPresenting ? handleXRFrame : null);
2040
+ if (!state.gl.xr.isPresenting) invalidate(state);
2041
+ };
2042
+
2043
+ // WebXR session manager
2044
+ const xr = {
2045
+ connect() {
2046
+ const gl = store.getState().gl;
2047
+ gl.xr.addEventListener('sessionstart', handleSessionChange);
2048
+ gl.xr.addEventListener('sessionend', handleSessionChange);
2049
+ },
2050
+ disconnect() {
2051
+ const gl = store.getState().gl;
2052
+ gl.xr.removeEventListener('sessionstart', handleSessionChange);
2053
+ gl.xr.removeEventListener('sessionend', handleSessionChange);
2054
+ }
2055
+ };
2056
+
2057
+ // Subscribe to WebXR session events
2058
+ if (gl.xr) xr.connect();
2059
+ state.set({
2060
+ xr
2061
+ });
2062
+ }
2063
+
2064
+ // Set shadowmap
2065
+ if (gl.shadowMap) {
2066
+ const oldEnabled = gl.shadowMap.enabled;
2067
+ const oldType = gl.shadowMap.type;
2068
+ gl.shadowMap.enabled = !!shadows;
2069
+ if (is.boo(shadows)) {
2070
+ gl.shadowMap.type = THREE__namespace.PCFSoftShadowMap;
2071
+ } else if (is.str(shadows)) {
2072
+ var _types$shadows;
2073
+ const types = {
2074
+ basic: THREE__namespace.BasicShadowMap,
2075
+ percentage: THREE__namespace.PCFShadowMap,
2076
+ soft: THREE__namespace.PCFSoftShadowMap,
2077
+ variance: THREE__namespace.VSMShadowMap
2078
+ };
2079
+ gl.shadowMap.type = (_types$shadows = types[shadows]) != null ? _types$shadows : THREE__namespace.PCFSoftShadowMap;
2080
+ } else if (is.obj(shadows)) {
2081
+ Object.assign(gl.shadowMap, shadows);
2082
+ }
2083
+ if (oldEnabled !== gl.shadowMap.enabled || oldType !== gl.shadowMap.type) gl.shadowMap.needsUpdate = true;
2084
+ }
2085
+
2086
+ // Safely set color management if available.
2087
+ // Avoid accessing THREE.ColorManagement to play nice with older versions
2088
+ const ColorManagement = getColorManagement();
2089
+ if (ColorManagement) {
2090
+ if ('enabled' in ColorManagement) ColorManagement.enabled = !legacy;else if ('legacyMode' in ColorManagement) ColorManagement.legacyMode = legacy;
2091
+ }
2092
+
2093
+ // Set color space and tonemapping preferences
2094
+ if (!configured) {
2095
+ const LinearEncoding = 3000;
2096
+ const sRGBEncoding = 3001;
2097
+ applyProps(gl, {
2098
+ outputEncoding: linear ? LinearEncoding : sRGBEncoding,
2099
+ toneMapping: flat ? THREE__namespace.NoToneMapping : THREE__namespace.ACESFilmicToneMapping
2100
+ });
2101
+ }
2102
+
2103
+ // Update color management state
2104
+ if (state.legacy !== legacy) state.set(() => ({
2105
+ legacy
2106
+ }));
2107
+ if (state.linear !== linear) state.set(() => ({
2108
+ linear
2109
+ }));
2110
+ if (state.flat !== flat) state.set(() => ({
2111
+ flat
2112
+ }));
2113
+
2114
+ // Set gl props
2115
+ if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, gl, shallowLoose)) applyProps(gl, glConfig);
2116
+ // Store events internally
2117
+ if (events && !state.events.handlers) state.set({
2118
+ events: events(store)
2119
+ });
2120
+ // Check size, allow it to take on container bounds initially
2121
+ const size = computeInitialSize(canvas, propsSize);
2122
+ if (!is.equ(size, state.size, shallowLoose)) {
2123
+ state.setSize(size.width, size.height, size.top, size.left);
2124
+ }
2125
+ // Check pixelratio
2126
+ if (dpr && state.viewport.dpr !== calculateDpr(dpr)) state.setDpr(dpr);
2127
+ // Check frameloop
2128
+ if (state.frameloop !== frameloop) state.setFrameloop(frameloop);
2129
+ // Check pointer missed
2130
+ if (!state.onPointerMissed) state.set({
2131
+ onPointerMissed
2132
+ });
2133
+ // Check performance
2134
+ if (performance && !is.equ(performance, state.performance, shallowLoose)) state.set(state => ({
2135
+ performance: {
2136
+ ...state.performance,
2137
+ ...performance
2138
+ }
2139
+ }));
2140
+
2141
+ // Create update stages. Only do this once on init
2142
+ if (state.internal.stages.length === 0) createStages(stages, store);
2143
+
2144
+ // Set locals
2145
+ onCreated = onCreatedCallback;
2146
+ configured = true;
2147
+ return this;
2148
+ },
2149
+ render(children) {
2150
+ // The root has to be configured before it can be rendered
2151
+ if (!configured) this.configure();
2152
+ reconciler.updateContainer( /*#__PURE__*/React__namespace.createElement(Provider, {
2153
+ store: store,
2154
+ children: children,
2155
+ onCreated: onCreated,
2156
+ rootElement: canvas
2157
+ }), fiber, null, () => undefined);
2158
+ return store;
2159
+ },
2160
+ unmount() {
2161
+ unmountComponentAtNode(canvas);
2162
+ }
2163
+ };
2164
+ }
2165
+ function render(children, canvas, config) {
2166
+ console.warn('R3F.render is no longer supported in React 18. Use createRoot instead!');
2167
+ const root = createRoot(canvas);
2168
+ root.configure(config);
2169
+ return root.render(children);
2170
+ }
2171
+ function Provider({
2172
+ store,
2173
+ children,
2174
+ onCreated,
2175
+ rootElement
2176
+ }) {
2177
+ useIsomorphicLayoutEffect(() => {
2178
+ const state = store.getState();
2179
+ // Flag the canvas active, rendering will now begin
2180
+ state.set(state => ({
2181
+ internal: {
2182
+ ...state.internal,
2183
+ active: true
2184
+ }
2185
+ }));
2186
+ // Notifiy that init is completed, the scene graph exists, but nothing has yet rendered
2187
+ if (onCreated) onCreated(state);
2188
+ // Connect events to the targets parent, this is done to ensure events are registered on
2189
+ // a shared target, and not on the canvas itself
2190
+ if (!store.getState().events.connected) state.events.connect == null ? void 0 : state.events.connect(rootElement);
2191
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2192
+ }, []);
2193
+ return /*#__PURE__*/React__namespace.createElement(context.Provider, {
2194
+ value: store
2195
+ }, children);
2196
+ }
2197
+ function unmountComponentAtNode(canvas, callback) {
2198
+ const root = _roots.get(canvas);
2199
+ const fiber = root == null ? void 0 : root.fiber;
2200
+ if (fiber) {
2201
+ const state = root == null ? void 0 : root.store.getState();
2202
+ if (state) state.internal.active = false;
2203
+ reconciler.updateContainer(null, fiber, null, () => {
2204
+ if (state) {
2205
+ setTimeout(() => {
2206
+ try {
2207
+ var _state$gl, _state$gl$renderLists, _state$gl2, _state$gl3;
2208
+ state.events.disconnect == null ? void 0 : state.events.disconnect();
2209
+ (_state$gl = state.gl) == null ? void 0 : (_state$gl$renderLists = _state$gl.renderLists) == null ? void 0 : _state$gl$renderLists.dispose == null ? void 0 : _state$gl$renderLists.dispose();
2210
+ (_state$gl2 = state.gl) == null ? void 0 : _state$gl2.forceContextLoss == null ? void 0 : _state$gl2.forceContextLoss();
2211
+ if ((_state$gl3 = state.gl) != null && _state$gl3.xr) state.xr.disconnect();
2212
+ dispose(state.scene);
2213
+ _roots.delete(canvas);
2214
+ if (callback) callback(canvas);
2215
+ } catch (e) {
2216
+ /* ... */
2217
+ }
2218
+ }, 500);
2219
+ }
2220
+ });
2221
+ }
2222
+ }
2223
+ function createPortal(children, container, state) {
2224
+ return /*#__PURE__*/React__namespace.createElement(Portal, {
2225
+ children: children,
2226
+ container: container,
2227
+ state: state
2228
+ });
2229
+ }
2230
+ function Portal({
2231
+ state = {},
2232
+ children,
2233
+ container
2234
+ }) {
2235
+ /** This has to be a component because it would not be able to call useThree/useStore otherwise since
2236
+ * if this is our environment, then we are not in r3f's renderer but in react-dom, it would trigger
2237
+ * the "R3F hooks can only be used within the Canvas component!" warning:
2238
+ * <Canvas>
2239
+ * {createPortal(...)} */
2240
+ const {
2241
+ events,
2242
+ size,
2243
+ ...rest
2244
+ } = state;
2245
+ const previousRoot = useStore();
2246
+ const [raycaster] = React__namespace.useState(() => new THREE__namespace.Raycaster());
2247
+ const [pointer] = React__namespace.useState(() => new THREE__namespace.Vector2());
2248
+ const inject = useMutableCallback((rootState, injectState) => {
2249
+ let viewport;
2250
+ if (injectState.camera && size) {
2251
+ const camera = injectState.camera;
2252
+ // Calculate the override viewport, if present
2253
+ viewport = rootState.viewport.getCurrentViewport(camera, new THREE__namespace.Vector3(), size);
2254
+ // Update the portal camera, if it differs from the previous layer
2255
+ if (camera !== rootState.camera) updateCamera(camera, size);
2256
+ }
2257
+ return {
2258
+ // The intersect consists of the previous root state
2259
+ ...rootState,
2260
+ get: injectState.get,
2261
+ set: injectState.set,
2262
+ // Portals have their own scene, which forms the root, a raycaster and a pointer
2263
+ scene: container,
2264
+ raycaster,
2265
+ pointer,
2266
+ mouse: pointer,
2267
+ // Their previous root is the layer before it
2268
+ previousRoot,
2269
+ // Events, size and viewport can be overridden by the inject layer
2270
+ events: {
2271
+ ...rootState.events,
2272
+ ...injectState.events,
2273
+ ...events
2274
+ },
2275
+ size: {
2276
+ ...rootState.size,
2277
+ ...size
2278
+ },
2279
+ viewport: {
2280
+ ...rootState.viewport,
2281
+ ...viewport
2282
+ },
2283
+ // Layers are allowed to override events
2284
+ setEvents: events => injectState.set(state => ({
2285
+ ...state,
2286
+ events: {
2287
+ ...state.events,
2288
+ ...events
2289
+ }
2290
+ }))
2291
+ };
2292
+ });
2293
+ const usePortalStore = React__namespace.useMemo(() => {
2294
+ const store = zustand.create((set, get) => ({
2295
+ ...rest,
2296
+ set,
2297
+ get
2298
+ }));
2299
+
2300
+ // Subscribe to previous root-state and copy changes over to the mirrored portal-state
2301
+ const onMutate = prev => store.setState(state => inject.current(prev, state));
2302
+ onMutate(previousRoot.getState());
2303
+ previousRoot.subscribe(onMutate);
2304
+ return store;
2305
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2306
+ }, [previousRoot, container]);
2307
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, reconciler.createPortal( /*#__PURE__*/React__namespace.createElement(context.Provider, {
2308
+ value: usePortalStore
2309
+ }, children), usePortalStore, null));
2310
+ }
2311
+ reconciler.injectIntoDevTools({
2312
+ bundleType: process.env.NODE_ENV === 'production' ? 0 : 1,
2313
+ rendererPackageName: '@react-three/fiber',
2314
+ version: React__namespace.version
2315
+ });
2316
+
2317
+ function createSubs(callback, subs) {
2318
+ const sub = {
2319
+ callback
2320
+ };
2321
+ subs.add(sub);
2322
+ return () => void subs.delete(sub);
2323
+ }
2324
+ const globalEffects = new Set();
2325
+ const globalAfterEffects = new Set();
2326
+ const globalTailEffects = new Set();
2327
+
2328
+ /**
2329
+ * Adds a global render callback which is called each frame.
2330
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addEffect
2331
+ */
2332
+ const addEffect = callback => createSubs(callback, globalEffects);
2333
+
2334
+ /**
2335
+ * Adds a global after-render callback which is called each frame.
2336
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addAfterEffect
2337
+ */
2338
+ const addAfterEffect = callback => createSubs(callback, globalAfterEffects);
2339
+
2340
+ /**
2341
+ * Adds a global callback which is called when rendering stops.
2342
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addTail
2343
+ */
2344
+ const addTail = callback => createSubs(callback, globalTailEffects);
2345
+ function run(effects, timestamp) {
2346
+ if (!effects.size) return;
2347
+ for (const {
2348
+ callback
2349
+ } of effects.values()) {
2350
+ callback(timestamp);
2351
+ }
2352
+ }
2353
+ function flushGlobalEffects(type, timestamp) {
2354
+ switch (type) {
2355
+ case 'before':
2356
+ return run(globalEffects, timestamp);
2357
+ case 'after':
2358
+ return run(globalAfterEffects, timestamp);
2359
+ case 'tail':
2360
+ return run(globalTailEffects, timestamp);
2361
+ }
2362
+ }
2363
+ function update(timestamp, state, frame) {
2364
+ // Run local effects
2365
+ let delta = state.clock.getDelta();
2366
+ // In frameloop='never' mode, clock times are updated using the provided timestamp
2367
+ if (state.frameloop === 'never' && typeof timestamp === 'number') {
2368
+ delta = timestamp - state.clock.elapsedTime;
2369
+ state.clock.oldTime = state.clock.elapsedTime;
2370
+ state.clock.elapsedTime = timestamp;
2371
+ } else {
2372
+ delta = Math.max(Math.min(delta, state.internal.maxDelta), 0);
2373
+ }
2374
+ // Call subscribers (useUpdate)
2375
+ for (const stage of state.internal.stages) {
2376
+ stage.frame(delta, frame);
2377
+ }
2378
+ state.internal.frames = Math.max(0, state.internal.frames - 1);
2379
+ return state.frameloop === 'always' ? 1 : state.internal.frames;
2380
+ }
2381
+ let running = false;
2382
+ let useFrameInProgress = false;
2383
+ let repeat;
2384
+ let frame;
2385
+ let state;
2386
+ function loop(timestamp) {
2387
+ frame = requestAnimationFrame(loop);
2388
+ running = true;
2389
+ repeat = 0;
2390
+
2391
+ // Run effects
2392
+ flushGlobalEffects('before', timestamp);
2393
+
2394
+ // Render all roots
2395
+ useFrameInProgress = true;
2396
+ for (const root of _roots.values()) {
2397
+ var _state$gl$xr;
2398
+ state = root.store.getState();
2399
+
2400
+ // If the frameloop is invalidated, do not run another frame
2401
+ if (state.internal.active && (state.frameloop === 'always' || state.internal.frames > 0) && !((_state$gl$xr = state.gl.xr) != null && _state$gl$xr.isPresenting)) {
2402
+ repeat += update(timestamp, state);
2403
+ }
2404
+ }
2405
+ useFrameInProgress = true;
2406
+
2407
+ // Run after-effects
2408
+ flushGlobalEffects('after', timestamp);
2409
+
2410
+ // Stop the loop if nothing invalidates it
2411
+ if (repeat === 0) {
2412
+ // Tail call effects, they are called when rendering stops
2413
+ flushGlobalEffects('tail', timestamp);
2414
+
2415
+ // Flag end of operation
2416
+ running = false;
2417
+ return cancelAnimationFrame(frame);
2418
+ }
2419
+ }
2420
+
2421
+ /**
2422
+ * Invalidates the view, requesting a frame to be rendered. Will globally invalidate unless passed a root's state.
2423
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#invalidate
2424
+ */
2425
+ function invalidate(state, frames = 1) {
2426
+ var _state$gl$xr2;
2427
+ if (!state) return _roots.forEach(root => invalidate(root.store.getState(), frames));
2428
+ if ((_state$gl$xr2 = state.gl.xr) != null && _state$gl$xr2.isPresenting || !state.internal.active || state.frameloop === 'never') return;
2429
+ if (frames > 1) {
2430
+ // legacy support for people using frames parameters
2431
+ // Increase frames, do not go higher than 60
2432
+ state.internal.frames = Math.min(60, state.internal.frames + frames);
2433
+ } else {
2434
+ if (useFrameInProgress) {
2435
+ //called from within a useFrame, it means the user wants an additional frame
2436
+ state.internal.frames = 2;
2437
+ } else {
2438
+ //the user need a new frame, no need to increment further than 1
2439
+ state.internal.frames = 1;
2440
+ }
2441
+ }
2442
+
2443
+ // If the render-loop isn't active, start it
2444
+ if (!running) {
2445
+ running = true;
2446
+ requestAnimationFrame(loop);
2447
+ }
2448
+ }
2449
+
2450
+ /**
2451
+ * Advances the frameloop and runs render effects, useful for when manually rendering via `frameloop="never"`.
2452
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#advance
2453
+ */
2454
+ function advance(timestamp, runGlobalEffects = true, state, frame) {
2455
+ if (runGlobalEffects) flushGlobalEffects('before', timestamp);
2456
+ if (!state) for (const root of _roots.values()) update(timestamp, root.store.getState());else update(timestamp, state, frame);
2457
+ if (runGlobalEffects) flushGlobalEffects('after', timestamp);
2458
+ }
2459
+
2460
+ exports.Block = Block;
2461
+ exports.ErrorBoundary = ErrorBoundary;
2462
+ exports.FixedStage = FixedStage;
2463
+ exports.Stage = Stage;
2464
+ exports.Stages = Stages;
2465
+ exports._roots = _roots;
2466
+ exports.act = act;
2467
+ exports.addAfterEffect = addAfterEffect;
2468
+ exports.addEffect = addEffect;
2469
+ exports.addTail = addTail;
2470
+ exports.advance = advance;
2471
+ exports.applyProps = applyProps;
2472
+ exports.buildGraph = buildGraph;
2473
+ exports.context = context;
2474
+ exports.createEvents = createEvents;
2475
+ exports.createPortal = createPortal;
2476
+ exports.createRoot = createRoot;
2477
+ exports.dispose = dispose;
2478
+ exports.extend = extend;
2479
+ exports.flushGlobalEffects = flushGlobalEffects;
2480
+ exports.getRootState = getRootState;
2481
+ exports.invalidate = invalidate;
2482
+ exports.isRef = isRef;
2483
+ exports.reconciler = reconciler;
2484
+ exports.render = render;
2485
+ exports.threeTypes = threeTypes;
2486
+ exports.unmountComponentAtNode = unmountComponentAtNode;
2487
+ exports.useBridge = useBridge;
2488
+ exports.useFrame = useFrame;
2489
+ exports.useGraph = useGraph;
2490
+ exports.useInstanceHandle = useInstanceHandle;
2491
+ exports.useIsomorphicLayoutEffect = useIsomorphicLayoutEffect;
2492
+ exports.useLoader = useLoader;
2493
+ exports.useMutableCallback = useMutableCallback;
2494
+ exports.useStore = useStore;
2495
+ exports.useThree = useThree;
2496
+ exports.useUpdate = useUpdate;