angular-three 2.0.0-beta.4 → 2.0.0-beta.41
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.
- package/README.md +4 -147
- package/esm2022/angular-three.mjs +1 -1
- package/esm2022/index.mjs +10 -11
- package/esm2022/lib/before-render.mjs +13 -0
- package/esm2022/lib/canvas.mjs +137 -161
- package/esm2022/lib/directives/args.mjs +13 -11
- package/esm2022/lib/directives/common.mjs +28 -27
- package/esm2022/lib/directives/key.mjs +29 -0
- package/esm2022/lib/directives/parent.mjs +13 -11
- package/esm2022/lib/dom/events.mjs +6 -1
- package/esm2022/lib/events.mjs +77 -60
- package/esm2022/lib/instance.mjs +65 -0
- package/esm2022/lib/loader.mjs +35 -41
- package/esm2022/lib/loop.mjs +6 -3
- package/esm2022/lib/portal.mjs +103 -112
- package/esm2022/lib/ref.mjs +48 -0
- package/esm2022/lib/renderer/catalogue.mjs +7 -0
- package/esm2022/lib/renderer/constants.mjs +21 -0
- package/esm2022/lib/renderer/index.mjs +419 -0
- package/esm2022/lib/renderer/store.mjs +148 -112
- package/esm2022/lib/renderer/utils.mjs +63 -48
- package/esm2022/lib/roots.mjs +249 -0
- package/esm2022/lib/routed-scene.mjs +11 -8
- package/esm2022/lib/store.mjs +207 -0
- package/esm2022/lib/three-types.mjs +2 -2
- package/esm2022/lib/types.mjs +1 -1
- package/esm2022/lib/utils/apply-props.mjs +23 -11
- package/esm2022/lib/utils/attach.mjs +2 -2
- package/esm2022/lib/utils/is.mjs +1 -1
- package/esm2022/lib/utils/make.mjs +1 -1
- package/esm2022/lib/utils/safe-detect-changes.mjs +15 -13
- package/esm2022/lib/utils/signal-store.mjs +99 -0
- package/esm2022/lib/utils/update.mjs +1 -1
- package/fesm2022/angular-three.mjs +2014 -1882
- package/fesm2022/angular-three.mjs.map +1 -1
- package/index.d.ts +9 -10
- package/lib/{di/before-render.d.ts → before-render.d.ts} +1 -1
- package/lib/canvas.d.ts +82 -12
- package/lib/directives/args.d.ts +2 -2
- package/lib/directives/common.d.ts +5 -1
- package/lib/directives/key.d.ts +10 -0
- package/lib/directives/parent.d.ts +5 -5
- package/lib/dom/events.d.ts +3 -2
- package/lib/events.d.ts +78 -2
- package/lib/instance.d.ts +36 -0
- package/lib/loader.d.ts +21 -3
- package/lib/loop.d.ts +68 -6
- package/lib/portal.d.ts +21 -12
- package/lib/{di/ref.d.ts → ref.d.ts} +3 -2
- package/lib/renderer/catalogue.d.ts +13 -0
- package/lib/renderer/constants.d.ts +20 -0
- package/lib/renderer/index.d.ts +5 -0
- package/lib/renderer/store.d.ts +21 -15
- package/lib/renderer/utils.d.ts +29 -19
- package/lib/roots.d.ts +11 -0
- package/lib/routed-scene.d.ts +1 -1
- package/lib/store.d.ts +147 -0
- package/lib/three-types.d.ts +5 -5
- package/lib/types.d.ts +1 -309
- package/lib/utils/apply-props.d.ts +4 -2
- package/lib/utils/attach.d.ts +5 -3
- package/lib/utils/is.d.ts +5 -4
- package/lib/utils/make.d.ts +12 -1
- package/lib/utils/safe-detect-changes.d.ts +2 -2
- package/lib/utils/signal-store.d.ts +17 -0
- package/lib/utils/update.d.ts +1 -1
- package/metadata.json +1 -1
- package/package.json +24 -5
- package/plugin/generators.json +57 -17
- package/plugin/migrations.json +27 -0
- package/plugin/package.json +22 -4
- package/plugin/src/generators/init/compat.d.ts +3 -1
- package/plugin/src/generators/init/compat.js +2 -2
- package/plugin/src/generators/init/compat.js.map +1 -1
- package/plugin/src/generators/init/files/experience/experience.component.html__tmpl__ +4 -0
- package/plugin/src/generators/init/files/experience/experience.component.ts__tmpl__ +18 -0
- package/plugin/src/generators/init/generator.d.ts +6 -0
- package/plugin/src/generators/init/generator.js +160 -0
- package/plugin/src/generators/init/generator.js.map +1 -0
- package/plugin/src/generators/init/schema.json +15 -4
- package/plugin/src/generators/init-cannon/compat.d.ts +2 -0
- package/plugin/src/generators/init-cannon/compat.js +6 -0
- package/plugin/src/generators/init-cannon/compat.js.map +1 -0
- package/plugin/src/generators/init-cannon/generator.d.ts +2 -0
- package/plugin/src/generators/init-cannon/generator.js +23 -0
- package/plugin/src/generators/init-cannon/generator.js.map +1 -0
- package/plugin/src/generators/init-cannon/schema.json +6 -0
- package/plugin/src/generators/init-postprocessing/compat.d.ts +2 -0
- package/plugin/src/generators/init-postprocessing/compat.js +6 -0
- package/plugin/src/generators/init-postprocessing/compat.js.map +1 -0
- package/plugin/src/generators/init-postprocessing/generator.d.ts +2 -0
- package/plugin/src/generators/init-postprocessing/generator.js +21 -0
- package/plugin/src/generators/init-postprocessing/generator.js.map +1 -0
- package/plugin/src/generators/init-postprocessing/schema.json +6 -0
- package/plugin/src/generators/init-rapier/compat.d.ts +2 -0
- package/plugin/src/generators/init-rapier/compat.js +6 -0
- package/plugin/src/generators/init-rapier/compat.js.map +1 -0
- package/plugin/src/generators/init-rapier/generator.d.ts +2 -0
- package/plugin/src/generators/init-rapier/generator.js +22 -0
- package/plugin/src/generators/init-rapier/generator.js.map +1 -0
- package/plugin/src/generators/init-rapier/schema.json +6 -0
- package/plugin/src/generators/init-soba/compat.d.ts +2 -0
- package/plugin/src/generators/init-soba/compat.js +6 -0
- package/plugin/src/generators/init-soba/compat.js.map +1 -0
- package/plugin/src/generators/init-soba/generator.d.ts +2 -0
- package/plugin/src/generators/init-soba/generator.js +27 -0
- package/plugin/src/generators/init-soba/generator.js.map +1 -0
- package/plugin/src/generators/init-soba/schema.json +6 -0
- package/plugin/src/generators/utils.d.ts +2 -0
- package/plugin/src/generators/utils.js +34 -0
- package/plugin/src/generators/utils.js.map +1 -0
- package/plugin/src/generators/versions.d.ts +14 -0
- package/plugin/src/generators/versions.js +18 -0
- package/plugin/src/generators/versions.js.map +1 -0
- package/plugin/src/index.d.ts +5 -1
- package/plugin/src/index.js +11 -3
- package/plugin/src/index.js.map +1 -1
- package/plugin/src/migrations/migrate-to-ngxtension/compat.d.ts +2 -0
- package/plugin/src/migrations/migrate-to-ngxtension/compat.js +6 -0
- package/plugin/src/migrations/migrate-to-ngxtension/compat.js.map +1 -0
- package/plugin/src/migrations/migrate-to-ngxtension/migrate-to-ngxtension.d.ts +2 -0
- package/plugin/src/migrations/migrate-to-ngxtension/migrate-to-ngxtension.js +55 -0
- package/plugin/src/migrations/migrate-to-ngxtension/migrate-to-ngxtension.js.map +1 -0
- package/web-types.json +1 -1
- package/esm2022/lib/di/before-render.mjs +0 -13
- package/esm2022/lib/di/catalogue.mjs +0 -7
- package/esm2022/lib/di/ref.mjs +0 -49
- package/esm2022/lib/directives/repeat.mjs +0 -18
- package/esm2022/lib/renderer/di.mjs +0 -3
- package/esm2022/lib/renderer/enums.mjs +0 -2
- package/esm2022/lib/renderer/provider.mjs +0 -18
- package/esm2022/lib/renderer/renderer.mjs +0 -368
- package/esm2022/lib/stores/signal.store.mjs +0 -79
- package/esm2022/lib/stores/store.mjs +0 -420
- package/esm2022/lib/utils/assert-in-injection-context.mjs +0 -14
- package/esm2022/lib/utils/instance.mjs +0 -63
- package/esm2022/lib/utils/signal.mjs +0 -24
- package/esm2022/lib/utils/timing.mjs +0 -21
- package/lib/di/catalogue.d.ts +0 -3
- package/lib/directives/repeat.d.ts +0 -7
- package/lib/renderer/di.d.ts +0 -2
- package/lib/renderer/enums.d.ts +0 -26
- package/lib/renderer/provider.d.ts +0 -8
- package/lib/renderer/renderer.d.ts +0 -49
- package/lib/stores/signal.store.d.ts +0 -21
- package/lib/stores/store.d.ts +0 -13
- package/lib/utils/assert-in-injection-context.d.ts +0 -2
- package/lib/utils/instance.d.ts +0 -4
- package/lib/utils/signal.d.ts +0 -2
- package/lib/utils/timing.d.ts +0 -4
- package/plugin/src/generators/init/init.d.ts +0 -5
- package/plugin/src/generators/init/init.js +0 -56
- package/plugin/src/generators/init/init.js.map +0 -1
|
@@ -1,109 +1,111 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { untracked, computed, signal, ElementRef, runInInjectionContext, inject, DestroyRef, effect, ChangeDetectorRef, InjectionToken, Injector, Optional, SkipSelf, ViewContainerRef, NgZone, TemplateRef, Directive, Input, EventEmitter, getDebugNode, RendererFactory2, Injectable, makeEnvironmentProviders, provideZoneChangeDetection, EnvironmentInjector, createEnvironmentInjector, Component, ChangeDetectionStrategy, Output, ViewChild, ContentChild } from '@angular/core';
|
|
3
|
+
import { assertInjector } from 'ngxtension/assert-injector';
|
|
4
|
+
import { DOCUMENT, NgIf } from '@angular/common';
|
|
5
|
+
import { createInjectionToken } from 'ngxtension/create-injection-token';
|
|
6
|
+
import { Subject, filter } from 'rxjs';
|
|
4
7
|
import * as THREE from 'three';
|
|
5
|
-
import {
|
|
8
|
+
import { provideResizeOptions, NgxResize } from 'ngxtension/resize';
|
|
6
9
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
7
10
|
import * as i1 from '@angular/router';
|
|
8
11
|
import { ActivationEnd, RouterOutlet } from '@angular/router';
|
|
9
|
-
import { filter } from 'rxjs';
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
const STORE_COMPUTED_KEY = '__ngt_signal_store_computed__';
|
|
14
|
+
const setter = (_source) => (state) => {
|
|
15
|
+
const updater = (previous) => {
|
|
16
|
+
const partial = typeof state === 'function' ? state(previous) : state;
|
|
17
|
+
Object.keys(partial).forEach((key) => {
|
|
18
|
+
const typedKey = key;
|
|
19
|
+
if (partial[typedKey] === undefined && previous[typedKey] != null) {
|
|
20
|
+
partial[typedKey] = previous[typedKey];
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
return partial;
|
|
22
24
|
};
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
untracked(() => {
|
|
26
|
+
_source.update((previous) => ({ ...previous, ...updater(previous) }));
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
const patcher = (_source) => (state) => {
|
|
30
|
+
const updater = (previous) => {
|
|
31
|
+
Object.keys(state).forEach((key) => {
|
|
32
|
+
const typedKey = key;
|
|
33
|
+
if (state[typedKey] === undefined && previous[typedKey] != null) {
|
|
34
|
+
state[typedKey] = previous[typedKey];
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
return state;
|
|
30
38
|
};
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
patch(state) {
|
|
80
|
-
const updater = (previous) => {
|
|
81
|
-
Object.keys(state).forEach((key) => {
|
|
82
|
-
const partialKey = key;
|
|
83
|
-
if (state[partialKey] === undefined && previous[partialKey] != null) {
|
|
84
|
-
state[partialKey] = previous[partialKey];
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
return state;
|
|
88
|
-
};
|
|
89
|
-
this.#state.update((previous) => ({ ...updater(previous), ...previous }));
|
|
39
|
+
untracked(() => {
|
|
40
|
+
_source.update((previous) => ({ ...updater(previous), ...previous }));
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
const getter = (_source) => (...keys) => {
|
|
44
|
+
const root = untracked(_source);
|
|
45
|
+
if (keys.length === 0)
|
|
46
|
+
return root;
|
|
47
|
+
return keys.reduce((value, key) => value[key], root);
|
|
48
|
+
};
|
|
49
|
+
const selector = (_state, computedCache) => (...keysAndOptions) => {
|
|
50
|
+
if (keysAndOptions.length === 0)
|
|
51
|
+
return _state;
|
|
52
|
+
if (keysAndOptions.length === 1 && typeof keysAndOptions[0] === 'object') {
|
|
53
|
+
const cachedKey = STORE_COMPUTED_KEY.concat(JSON.stringify(keysAndOptions[0]));
|
|
54
|
+
if (!computedCache.has(cachedKey)) {
|
|
55
|
+
computedCache.set(cachedKey, computed(_state, keysAndOptions));
|
|
56
|
+
}
|
|
57
|
+
return computedCache.get(cachedKey);
|
|
58
|
+
}
|
|
59
|
+
const [keys, options] = parseStoreOptions(keysAndOptions);
|
|
60
|
+
const joinedKeys = keys.join('-');
|
|
61
|
+
const cachedKeys = joinedKeys.concat(options ? JSON.stringify(options) : '');
|
|
62
|
+
if (!computedCache.has(cachedKeys)) {
|
|
63
|
+
computedCache.set(cachedKeys, computed(() => keys.reduce((value, key) => value[key], _state()), options));
|
|
64
|
+
}
|
|
65
|
+
return computedCache.get(cachedKeys);
|
|
66
|
+
};
|
|
67
|
+
function signalStore(initialState = {}, options) {
|
|
68
|
+
let source;
|
|
69
|
+
let set;
|
|
70
|
+
let get;
|
|
71
|
+
let patch;
|
|
72
|
+
let select;
|
|
73
|
+
let state;
|
|
74
|
+
const computedCache = new Map();
|
|
75
|
+
if (!options) {
|
|
76
|
+
options = { equal: Object.is };
|
|
77
|
+
}
|
|
78
|
+
if (typeof initialState === 'function') {
|
|
79
|
+
source = signal({}, options);
|
|
80
|
+
state = source.asReadonly();
|
|
81
|
+
get = getter(source);
|
|
82
|
+
set = setter(source);
|
|
83
|
+
patch = patcher(source);
|
|
84
|
+
select = selector(state, computedCache);
|
|
85
|
+
source.set(initialState({ set, get, patch, select }));
|
|
90
86
|
}
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
else {
|
|
88
|
+
source = signal(initialState, options);
|
|
89
|
+
state = source.asReadonly();
|
|
90
|
+
get = getter(source);
|
|
91
|
+
set = setter(source);
|
|
92
|
+
patch = patcher(source);
|
|
93
|
+
select = selector(state, computedCache);
|
|
94
|
+
}
|
|
95
|
+
const store = { select, get, set, patch, state };
|
|
96
|
+
// NOTE: internal _snapshot to debug current state
|
|
97
|
+
Object.defineProperty(store, '_snapshot', {
|
|
98
|
+
get: state,
|
|
99
|
+
configurable: false,
|
|
100
|
+
enumerable: false,
|
|
101
|
+
});
|
|
102
|
+
return store;
|
|
93
103
|
}
|
|
94
|
-
|
|
95
|
-
type: Injectable
|
|
96
|
-
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
|
|
97
|
-
type: Optional
|
|
98
|
-
}, {
|
|
99
|
-
type: Inject,
|
|
100
|
-
args: ['INITIAL_STATE']
|
|
101
|
-
}] }]; } });
|
|
102
|
-
function parseOptions(keysAndOptions) {
|
|
104
|
+
function parseStoreOptions(keysAndOptions) {
|
|
103
105
|
if (typeof keysAndOptions.at(-1) === 'object') {
|
|
104
106
|
return [keysAndOptions.slice(0, -1), keysAndOptions.at(-1)];
|
|
105
107
|
}
|
|
106
|
-
return [keysAndOptions];
|
|
108
|
+
return [keysAndOptions, { equal: Object.is }];
|
|
107
109
|
}
|
|
108
110
|
|
|
109
111
|
const is = {
|
|
@@ -201,34 +203,37 @@ function invalidateInstance(instance) {
|
|
|
201
203
|
function prepare(object, localState) {
|
|
202
204
|
const instance = object;
|
|
203
205
|
if (localState?.primitive || !instance.__ngt__) {
|
|
204
|
-
const { objects =
|
|
206
|
+
const { objects = signal([]), nonObjects = signal([]), ...rest } = localState || {};
|
|
205
207
|
instance.__ngt__ = {
|
|
206
208
|
previousAttach: null,
|
|
207
209
|
store: null,
|
|
208
|
-
parent:
|
|
210
|
+
parent: signal(null),
|
|
209
211
|
memoized: {},
|
|
210
212
|
eventCount: 0,
|
|
211
213
|
handlers: {},
|
|
212
214
|
objects,
|
|
213
215
|
nonObjects,
|
|
214
|
-
nativeProps:
|
|
216
|
+
nativeProps: signalStore(),
|
|
215
217
|
add: (object, type) => {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
218
|
+
untracked(() => {
|
|
219
|
+
const current = instance.__ngt__[type]();
|
|
220
|
+
const foundIndex = current.indexOf((obj) => obj === object);
|
|
221
|
+
if (foundIndex > -1) {
|
|
222
|
+
// if we add an object with the same reference, then we switch it out
|
|
223
|
+
current.splice(foundIndex, 1, object);
|
|
224
|
+
instance.__ngt__[type].set(current);
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
instance.__ngt__[type].update((prev) => [...prev, object]);
|
|
228
|
+
}
|
|
229
|
+
notifyAncestors(instance.__ngt__.parent());
|
|
230
|
+
});
|
|
228
231
|
},
|
|
229
232
|
remove: (object, type) => {
|
|
230
|
-
|
|
231
|
-
|
|
233
|
+
untracked(() => {
|
|
234
|
+
instance.__ngt__[type].update((prev) => prev.filter((o) => o !== object));
|
|
235
|
+
notifyAncestors(instance.__ngt__.parent());
|
|
236
|
+
});
|
|
232
237
|
},
|
|
233
238
|
...rest,
|
|
234
239
|
};
|
|
@@ -243,7 +248,129 @@ function notifyAncestors(instance) {
|
|
|
243
248
|
localState.objects.update((prev) => prev);
|
|
244
249
|
if (localState.nonObjects)
|
|
245
250
|
localState.nonObjects.update((prev) => prev);
|
|
246
|
-
notifyAncestors(
|
|
251
|
+
notifyAncestors(localState.parent());
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// This function prepares a set of changes to be applied to the instance
|
|
255
|
+
function diffProps(instance, props) {
|
|
256
|
+
const propsEntries = Object.entries(props);
|
|
257
|
+
const changes = [];
|
|
258
|
+
for (const [propKey, propValue] of propsEntries) {
|
|
259
|
+
let key = propKey;
|
|
260
|
+
if (is.colorSpaceExist(instance)) {
|
|
261
|
+
if (propKey === 'encoding') {
|
|
262
|
+
key = 'colorSpace';
|
|
263
|
+
}
|
|
264
|
+
else if (propKey === 'outputEncoding') {
|
|
265
|
+
key = 'outputColorSpace';
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (is.equ(propValue, instance[key]))
|
|
269
|
+
continue;
|
|
270
|
+
changes.push([propKey, propValue]);
|
|
271
|
+
}
|
|
272
|
+
return changes;
|
|
273
|
+
}
|
|
274
|
+
// This function applies a set of changes to the instance
|
|
275
|
+
function applyProps(instance, props) {
|
|
276
|
+
// if props is empty
|
|
277
|
+
if (!Object.keys(props).length)
|
|
278
|
+
return instance;
|
|
279
|
+
// Filter equals, events and reserved props
|
|
280
|
+
// filter equals, events , and reserved props
|
|
281
|
+
const localState = getLocalState(instance);
|
|
282
|
+
const rootState = localState.store?.get();
|
|
283
|
+
const changes = diffProps(instance, props);
|
|
284
|
+
for (let i = 0; i < changes.length; i++) {
|
|
285
|
+
let [key, value] = changes[i];
|
|
286
|
+
// Alias (output)encoding => (output)colorSpace (since r152)
|
|
287
|
+
// https://github.com/pmndrs/react-three-fiber/pull/2829
|
|
288
|
+
if (is.colorSpaceExist(instance)) {
|
|
289
|
+
const sRGBEncoding = 3001;
|
|
290
|
+
const SRGBColorSpace = 'srgb';
|
|
291
|
+
const LinearSRGBColorSpace = 'srgb-linear';
|
|
292
|
+
if (key === 'encoding') {
|
|
293
|
+
key = 'colorSpace';
|
|
294
|
+
value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
|
|
295
|
+
}
|
|
296
|
+
else if (key === 'outputEncoding') {
|
|
297
|
+
key = 'outputColorSpace';
|
|
298
|
+
value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
const currentInstance = instance;
|
|
302
|
+
const targetProp = currentInstance[key];
|
|
303
|
+
// special treatmen for objects with support for set/copy, and layers
|
|
304
|
+
if (targetProp && targetProp['set'] && (targetProp['copy'] || targetProp instanceof THREE.Layers)) {
|
|
305
|
+
const isColor = targetProp instanceof THREE.Color;
|
|
306
|
+
// if value is an array
|
|
307
|
+
if (Array.isArray(value)) {
|
|
308
|
+
if (targetProp['fromArray'])
|
|
309
|
+
targetProp['fromArray'](value);
|
|
310
|
+
else
|
|
311
|
+
targetProp['set'](...value);
|
|
312
|
+
}
|
|
313
|
+
// test again target.copy
|
|
314
|
+
else if (targetProp['copy'] &&
|
|
315
|
+
value &&
|
|
316
|
+
value.constructor &&
|
|
317
|
+
targetProp.constructor.name === value.constructor.name) {
|
|
318
|
+
targetProp['copy'](value);
|
|
319
|
+
if (!THREE.ColorManagement && !rootState.linear && isColor)
|
|
320
|
+
targetProp['convertSRGBToLinear']();
|
|
321
|
+
}
|
|
322
|
+
// if nothing else fits, just set the single value, ignore undefined
|
|
323
|
+
else if (value !== undefined) {
|
|
324
|
+
const isColor = targetProp instanceof THREE.Color;
|
|
325
|
+
// allow setting array scalars
|
|
326
|
+
if (!isColor && targetProp['setScalar'])
|
|
327
|
+
targetProp['setScalar'](value);
|
|
328
|
+
// layers have no copy function, copy the mask
|
|
329
|
+
else if (targetProp instanceof THREE.Layers && value instanceof THREE.Layers)
|
|
330
|
+
targetProp.mask = value.mask;
|
|
331
|
+
// otherwise just set ...
|
|
332
|
+
else
|
|
333
|
+
targetProp['set'](value);
|
|
334
|
+
// auto-convert srgb
|
|
335
|
+
if (!THREE.ColorManagement && !rootState?.linear && isColor)
|
|
336
|
+
targetProp.convertSRGBToLinear();
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
// else just overwrite the value
|
|
340
|
+
else {
|
|
341
|
+
currentInstance[key] = value;
|
|
342
|
+
// auto-convert srgb textures
|
|
343
|
+
if (currentInstance[key] instanceof THREE.Texture &&
|
|
344
|
+
currentInstance[key].format === THREE.RGBAFormat &&
|
|
345
|
+
currentInstance[key].type === THREE.UnsignedByteType) {
|
|
346
|
+
const texture = currentInstance[key];
|
|
347
|
+
if (rootState?.gl) {
|
|
348
|
+
if (is.colorSpaceExist(texture) && is.colorSpaceExist(rootState.gl))
|
|
349
|
+
texture.colorSpace = rootState.gl.outputColorSpace;
|
|
350
|
+
else
|
|
351
|
+
texture.encoding = rootState.gl.outputEncoding;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
checkUpdate(currentInstance[key]);
|
|
356
|
+
checkUpdate(targetProp);
|
|
357
|
+
invalidateInstance(instance);
|
|
358
|
+
}
|
|
359
|
+
const instanceHandlers = localState.eventCount;
|
|
360
|
+
const parent = localState.parent ? untracked(localState.parent) : null;
|
|
361
|
+
if (parent && rootState.internal && instance['raycast'] && instanceHandlers !== localState.eventCount) {
|
|
362
|
+
// Pre-emptively remove the instance from the interaction manager
|
|
363
|
+
const index = rootState.internal.interaction.indexOf(instance);
|
|
364
|
+
if (index > -1)
|
|
365
|
+
rootState.internal.interaction.splice(index, 1);
|
|
366
|
+
// Add the instance to the interaction manager only when it has handlers
|
|
367
|
+
if (localState.eventCount)
|
|
368
|
+
rootState.internal.interaction.push(instance);
|
|
369
|
+
}
|
|
370
|
+
if (parent && localState.afterUpdate && localState.afterUpdate.observed && changes.length) {
|
|
371
|
+
localState.afterUpdate.emit(instance);
|
|
372
|
+
}
|
|
373
|
+
return instance;
|
|
247
374
|
}
|
|
248
375
|
|
|
249
376
|
const idCache = {};
|
|
@@ -295,1187 +422,1222 @@ function makeObjectGraph(object) {
|
|
|
295
422
|
return data;
|
|
296
423
|
}
|
|
297
424
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
425
|
+
const shallowLoose = { objects: 'shallow', strict: false };
|
|
426
|
+
const roots = new Map();
|
|
427
|
+
function injectCanvasRootInitializer(injector) {
|
|
428
|
+
injector = assertInjector(injectCanvasRootInitializer, injector);
|
|
429
|
+
return runInInjectionContext(injector, () => {
|
|
430
|
+
const injectedStore = injectNgtStore();
|
|
431
|
+
const loop = injectNgtLoop();
|
|
432
|
+
const destroyRef = inject(DestroyRef);
|
|
433
|
+
return (canvas) => {
|
|
434
|
+
const exist = roots.has(canvas);
|
|
435
|
+
let store = roots.get(canvas);
|
|
436
|
+
if (store) {
|
|
437
|
+
console.warn('[NGT] Same canvas root is being created twice');
|
|
438
|
+
}
|
|
439
|
+
store ||= injectedStore;
|
|
440
|
+
if (!store) {
|
|
441
|
+
throw new Error('[NGT] No store initialized');
|
|
442
|
+
}
|
|
443
|
+
if (!exist) {
|
|
444
|
+
roots.set(canvas, store);
|
|
445
|
+
}
|
|
446
|
+
let isConfigured = false;
|
|
447
|
+
let invalidateRef;
|
|
448
|
+
destroyRef.onDestroy(() => invalidateRef?.destroy());
|
|
449
|
+
return {
|
|
450
|
+
isConfigured,
|
|
451
|
+
destroy: (timeout = 500) => {
|
|
452
|
+
const root = roots.get(canvas);
|
|
453
|
+
if (root) {
|
|
454
|
+
root.set((state) => ({ internal: { ...state.internal, active: false } }));
|
|
455
|
+
setTimeout(() => {
|
|
456
|
+
try {
|
|
457
|
+
const state = root.get();
|
|
458
|
+
state.events.disconnect?.();
|
|
459
|
+
state.gl?.renderLists?.dispose?.();
|
|
460
|
+
state.gl?.forceContextLoss?.();
|
|
461
|
+
if (state.gl?.xr)
|
|
462
|
+
state.xr.disconnect();
|
|
463
|
+
dispose(state);
|
|
464
|
+
roots.delete(canvas);
|
|
465
|
+
}
|
|
466
|
+
catch (e) {
|
|
467
|
+
console.error('[NGT] Unexpected error while destroying Canvas Root', e);
|
|
468
|
+
}
|
|
469
|
+
}, timeout);
|
|
470
|
+
}
|
|
471
|
+
},
|
|
472
|
+
configure: (inputs) => {
|
|
473
|
+
const { gl: glOptions, size: sizeOptions, camera: cameraOptions, raycaster: raycasterOptions, scene: sceneOptions, events, orthographic, lookAt, shadows, linear, legacy, flat, dpr, frameloop, performance, } = inputs;
|
|
474
|
+
const state = store.get();
|
|
475
|
+
const stateToUpdate = {};
|
|
476
|
+
// setup renderer
|
|
477
|
+
let gl = state.gl;
|
|
478
|
+
if (!state.gl)
|
|
479
|
+
stateToUpdate.gl = gl = makeDefaultRenderer(glOptions, canvas);
|
|
480
|
+
// setup raycaster
|
|
481
|
+
let raycaster = state.raycaster;
|
|
482
|
+
if (!raycaster)
|
|
483
|
+
stateToUpdate.raycaster = raycaster = new THREE.Raycaster();
|
|
484
|
+
// set raycaster options
|
|
485
|
+
const { params, ...options } = raycasterOptions || {};
|
|
486
|
+
if (!is.equ(options, raycaster, shallowLoose))
|
|
487
|
+
applyProps(raycaster, { ...options });
|
|
488
|
+
if (!is.equ(params, raycaster.params, shallowLoose)) {
|
|
489
|
+
applyProps(raycaster, { params: { ...raycaster.params, ...(params || {}) } });
|
|
490
|
+
}
|
|
491
|
+
// create default camera
|
|
492
|
+
if (!state.camera) {
|
|
493
|
+
const isCamera = is.camera(cameraOptions);
|
|
494
|
+
let camera = isCamera ? cameraOptions : makeDefaultCamera(orthographic || false, state.size);
|
|
495
|
+
if (!isCamera) {
|
|
496
|
+
if (cameraOptions)
|
|
497
|
+
applyProps(camera, cameraOptions);
|
|
498
|
+
// set position.z
|
|
499
|
+
if (!cameraOptions?.position)
|
|
500
|
+
camera.position.z = 5;
|
|
501
|
+
// always look at center or passed-in lookAt by default
|
|
502
|
+
if (!cameraOptions?.rotation && !cameraOptions?.quaternion) {
|
|
503
|
+
if (Array.isArray(lookAt))
|
|
504
|
+
camera.lookAt(lookAt[0], lookAt[1], lookAt[2]);
|
|
505
|
+
else if (lookAt instanceof THREE.Vector3)
|
|
506
|
+
camera.lookAt(lookAt);
|
|
507
|
+
else
|
|
508
|
+
camera.lookAt(0, 0, 0);
|
|
509
|
+
}
|
|
510
|
+
// update projection matrix after applyprops
|
|
511
|
+
camera.updateProjectionMatrix?.();
|
|
512
|
+
}
|
|
513
|
+
if (!is.instance(camera))
|
|
514
|
+
camera = prepare(camera, { store });
|
|
515
|
+
stateToUpdate.camera = camera;
|
|
516
|
+
}
|
|
517
|
+
// Set up scene (one time only!)
|
|
518
|
+
if (!state.scene) {
|
|
519
|
+
let scene;
|
|
520
|
+
if (sceneOptions instanceof THREE.Scene) {
|
|
521
|
+
scene = prepare(sceneOptions, { store });
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
scene = prepare(new THREE.Scene(), { store });
|
|
525
|
+
if (sceneOptions)
|
|
526
|
+
applyProps(scene, sceneOptions);
|
|
527
|
+
}
|
|
528
|
+
stateToUpdate.scene = scene;
|
|
529
|
+
}
|
|
530
|
+
// Set up XR (one time only!)
|
|
531
|
+
if (!state.xr) {
|
|
532
|
+
// Handle frame behavior in WebXR
|
|
533
|
+
const handleXRFrame = (timestamp, frame) => {
|
|
534
|
+
const state = store.get();
|
|
535
|
+
if (state.frameloop === 'never')
|
|
536
|
+
return;
|
|
537
|
+
loop.advance(timestamp, true, store, frame);
|
|
538
|
+
};
|
|
539
|
+
// Toggle render switching on session
|
|
540
|
+
const handleSessionChange = () => {
|
|
541
|
+
const state = store.get();
|
|
542
|
+
state.gl.xr.enabled = state.gl.xr.isPresenting;
|
|
543
|
+
state.gl.xr.setAnimationLoop(state.gl.xr.isPresenting ? handleXRFrame : null);
|
|
544
|
+
if (!state.gl.xr.isPresenting)
|
|
545
|
+
loop.invalidate(store);
|
|
546
|
+
};
|
|
547
|
+
// WebXR session manager
|
|
548
|
+
const xr = {
|
|
549
|
+
connect: () => {
|
|
550
|
+
gl.xr.addEventListener('sessionstart', handleSessionChange);
|
|
551
|
+
gl.xr.addEventListener('sessionend', handleSessionChange);
|
|
552
|
+
},
|
|
553
|
+
disconnect: () => {
|
|
554
|
+
gl.xr.removeEventListener('sessionstart', handleSessionChange);
|
|
555
|
+
gl.xr.removeEventListener('sessionend', handleSessionChange);
|
|
556
|
+
},
|
|
557
|
+
};
|
|
558
|
+
// Subscribe to WebXR session events
|
|
559
|
+
if (gl.xr && typeof gl.xr.addEventListener === 'function')
|
|
560
|
+
xr.connect();
|
|
561
|
+
stateToUpdate.xr = xr;
|
|
562
|
+
}
|
|
563
|
+
// Set shadowmap
|
|
564
|
+
if (gl.shadowMap) {
|
|
565
|
+
const oldEnabled = gl.shadowMap.enabled;
|
|
566
|
+
const oldType = gl.shadowMap.type;
|
|
567
|
+
gl.shadowMap.enabled = !!shadows;
|
|
568
|
+
if (typeof shadows === 'boolean') {
|
|
569
|
+
gl.shadowMap.type = THREE.PCFSoftShadowMap;
|
|
570
|
+
}
|
|
571
|
+
else if (typeof shadows === 'string') {
|
|
572
|
+
const types = {
|
|
573
|
+
basic: THREE.BasicShadowMap,
|
|
574
|
+
percentage: THREE.PCFShadowMap,
|
|
575
|
+
soft: THREE.PCFSoftShadowMap,
|
|
576
|
+
variance: THREE.VSMShadowMap,
|
|
577
|
+
};
|
|
578
|
+
gl.shadowMap.type = types[shadows] ?? THREE.PCFSoftShadowMap;
|
|
579
|
+
}
|
|
580
|
+
else if (is.obj(shadows)) {
|
|
581
|
+
Object.assign(gl.shadowMap, shadows);
|
|
582
|
+
}
|
|
583
|
+
if (oldEnabled !== gl.shadowMap.enabled || oldType !== gl.shadowMap.type)
|
|
584
|
+
checkNeedsUpdate(gl.shadowMap);
|
|
585
|
+
}
|
|
586
|
+
// Safely set color management if available.
|
|
587
|
+
// Avoid accessing THREE.ColorManagement to play nice with older versions
|
|
588
|
+
if (THREE.ColorManagement) {
|
|
589
|
+
const ColorManagement = THREE.ColorManagement;
|
|
590
|
+
if ('enabled' in ColorManagement)
|
|
591
|
+
ColorManagement['enabled'] = !legacy ?? false;
|
|
592
|
+
else if ('legacyMode' in ColorManagement)
|
|
593
|
+
ColorManagement['legacyMode'] = legacy ?? true;
|
|
594
|
+
}
|
|
595
|
+
// set color space and tonemapping preferences
|
|
596
|
+
const LinearEncoding = 3000;
|
|
597
|
+
const sRGBEncoding = 3001;
|
|
598
|
+
applyProps(gl, {
|
|
599
|
+
outputEncoding: linear ? LinearEncoding : sRGBEncoding,
|
|
600
|
+
toneMapping: flat ? THREE.NoToneMapping : THREE.ACESFilmicToneMapping,
|
|
601
|
+
});
|
|
602
|
+
// Update color management state
|
|
603
|
+
if (state.legacy !== legacy)
|
|
604
|
+
stateToUpdate.legacy = legacy;
|
|
605
|
+
if (state.linear !== linear)
|
|
606
|
+
stateToUpdate.linear = linear;
|
|
607
|
+
if (state.flat !== flat)
|
|
608
|
+
stateToUpdate.flat = flat;
|
|
609
|
+
// Set gl props
|
|
610
|
+
gl.setClearAlpha(0);
|
|
611
|
+
gl.setPixelRatio(makeDpr(state.viewport.dpr));
|
|
612
|
+
gl.setSize(state.size.width, state.size.height);
|
|
613
|
+
if (is.obj(glOptions) &&
|
|
614
|
+
!(typeof glOptions === 'function') &&
|
|
615
|
+
!is.renderer(glOptions) &&
|
|
616
|
+
!is.equ(glOptions, gl, shallowLoose)) {
|
|
617
|
+
applyProps(gl, glOptions);
|
|
618
|
+
}
|
|
619
|
+
// Store events internally
|
|
620
|
+
if (events && !state.events.handlers)
|
|
621
|
+
stateToUpdate.events = events(store);
|
|
622
|
+
// Check performance
|
|
623
|
+
if (performance && !is.equ(performance, state.performance, shallowLoose)) {
|
|
624
|
+
stateToUpdate.performance = { ...state.performance, ...performance };
|
|
625
|
+
}
|
|
626
|
+
store.set(stateToUpdate);
|
|
627
|
+
// Check size, allow it to take on container bounds initially
|
|
628
|
+
const size = computeInitialSize(canvas, sizeOptions);
|
|
629
|
+
if (!is.equ(size, state.size, shallowLoose)) {
|
|
630
|
+
state.setSize(size.width, size.height, size.top, size.left);
|
|
631
|
+
}
|
|
632
|
+
// Check pixelratio
|
|
633
|
+
if (dpr && state.viewport.dpr !== makeDpr(dpr))
|
|
634
|
+
state.setDpr(dpr);
|
|
635
|
+
// Check frameloop
|
|
636
|
+
if (state.frameloop !== frameloop)
|
|
637
|
+
state.setFrameloop(frameloop);
|
|
638
|
+
isConfigured = true;
|
|
639
|
+
invalidateRef?.destroy();
|
|
640
|
+
invalidateRef = effect(() => void store.state().invalidate(), { manualCleanup: true, injector });
|
|
641
|
+
},
|
|
642
|
+
};
|
|
643
|
+
};
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
function computeInitialSize(canvas, defaultSize) {
|
|
647
|
+
if (defaultSize)
|
|
648
|
+
return defaultSize;
|
|
649
|
+
if (canvas instanceof HTMLCanvasElement && canvas.parentElement) {
|
|
650
|
+
return canvas.parentElement.getBoundingClientRect();
|
|
307
651
|
}
|
|
652
|
+
return { width: 0, height: 0, top: 0, left: 0 };
|
|
308
653
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
// clear out intersects, they are outdated by now
|
|
317
|
-
internal.hovered.delete(key);
|
|
318
|
-
}
|
|
319
|
-
});
|
|
320
|
-
internal.capturedMap.forEach((captures, pointerId) => {
|
|
321
|
-
releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
|
|
322
|
-
});
|
|
323
|
-
if (store.get('previousStore')) {
|
|
324
|
-
removeInteractivity(store.get('previousStore'), object);
|
|
654
|
+
// Disposes an object and all its properties
|
|
655
|
+
function dispose(obj) {
|
|
656
|
+
if (obj.dispose && obj.type !== 'Scene')
|
|
657
|
+
obj.dispose();
|
|
658
|
+
for (const p in obj) {
|
|
659
|
+
p.dispose?.();
|
|
660
|
+
delete obj[p];
|
|
325
661
|
}
|
|
326
662
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
663
|
+
|
|
664
|
+
function createSubs(callback, subs) {
|
|
665
|
+
const sub = { callback };
|
|
666
|
+
subs.add(sub);
|
|
667
|
+
return () => void subs.delete(sub);
|
|
668
|
+
}
|
|
669
|
+
const globalEffects = new Set();
|
|
670
|
+
const globalAfterEffects = new Set();
|
|
671
|
+
const globalTailEffects = new Set();
|
|
672
|
+
/**
|
|
673
|
+
* Adds a global render callback which is called each frame.
|
|
674
|
+
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addEffect
|
|
675
|
+
*/
|
|
676
|
+
const addEffect = (callback) => createSubs(callback, globalEffects);
|
|
677
|
+
/**
|
|
678
|
+
* Adds a global after-render callback which is called each frame.
|
|
679
|
+
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addAfterEffect
|
|
680
|
+
*/
|
|
681
|
+
const addAfterEffect = (callback) => createSubs(callback, globalAfterEffects);
|
|
682
|
+
/**
|
|
683
|
+
* Adds a global callback which is called when rendering stops.
|
|
684
|
+
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addTail
|
|
685
|
+
*/
|
|
686
|
+
const addTail = (callback) => createSubs(callback, globalTailEffects);
|
|
687
|
+
function run(effects, timestamp) {
|
|
688
|
+
if (!effects.size)
|
|
689
|
+
return;
|
|
690
|
+
for (const { callback } of effects.values()) {
|
|
691
|
+
callback(timestamp);
|
|
334
692
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
return
|
|
340
|
-
|
|
693
|
+
}
|
|
694
|
+
function flushGlobalEffects(type, timestamp) {
|
|
695
|
+
switch (type) {
|
|
696
|
+
case 'before':
|
|
697
|
+
return run(globalEffects, timestamp);
|
|
698
|
+
case 'after':
|
|
699
|
+
return run(globalAfterEffects, timestamp);
|
|
700
|
+
case 'tail':
|
|
701
|
+
return run(globalTailEffects, timestamp);
|
|
341
702
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
//
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
if (!aState || !bState)
|
|
385
|
-
return a.distance - b.distance;
|
|
386
|
-
return bState.events.priority - aState.events.priority || a.distance - b.distance;
|
|
387
|
-
})
|
|
388
|
-
// filter out duplicates
|
|
389
|
-
.filter((item) => {
|
|
390
|
-
const id = makeId(item);
|
|
391
|
-
if (duplicates.has(id))
|
|
392
|
-
return false;
|
|
393
|
-
duplicates.add(id);
|
|
394
|
-
return true;
|
|
395
|
-
});
|
|
396
|
-
// allow custom userland intersect sort order, this likely only makes sense on the root
|
|
397
|
-
if (state.events.filter)
|
|
398
|
-
hits = state.events.filter(hits, store);
|
|
399
|
-
// bubble up the events, find the event source
|
|
400
|
-
for (const hit of hits) {
|
|
401
|
-
let eventObject = hit.object;
|
|
402
|
-
// bubble event up
|
|
403
|
-
while (eventObject) {
|
|
404
|
-
if (getLocalState(eventObject).eventCount) {
|
|
405
|
-
intersections.push({ ...hit, eventObject });
|
|
406
|
-
}
|
|
407
|
-
eventObject = eventObject.parent;
|
|
703
|
+
}
|
|
704
|
+
function render(timestamp, store, frame) {
|
|
705
|
+
const state = store.get();
|
|
706
|
+
// Run local effects
|
|
707
|
+
let delta = state.clock.getDelta();
|
|
708
|
+
// In frameloop='never' mode, clock times are updated using the provided timestamp
|
|
709
|
+
if (state.frameloop === 'never' && typeof timestamp === 'number') {
|
|
710
|
+
delta = timestamp - state.clock.elapsedTime;
|
|
711
|
+
state.clock.oldTime = state.clock.elapsedTime;
|
|
712
|
+
state.clock.elapsedTime = timestamp;
|
|
713
|
+
}
|
|
714
|
+
// Call subscribers (useFrame)
|
|
715
|
+
const subscribers = state.internal.subscribers;
|
|
716
|
+
for (let i = 0; i < subscribers.length; i++) {
|
|
717
|
+
const subscription = subscribers[i];
|
|
718
|
+
subscription.callback({ ...subscription.store.get(), delta, frame });
|
|
719
|
+
}
|
|
720
|
+
// Render content
|
|
721
|
+
if (!state.internal.priority && state.gl.render)
|
|
722
|
+
state.gl.render(state.scene, state.camera);
|
|
723
|
+
// Decrease frame count
|
|
724
|
+
state.internal.frames = Math.max(0, state.internal.frames - 1);
|
|
725
|
+
return state.frameloop === 'always' ? 1 : state.internal.frames;
|
|
726
|
+
}
|
|
727
|
+
function createLoop(roots) {
|
|
728
|
+
let running = false;
|
|
729
|
+
let repeat;
|
|
730
|
+
let frame;
|
|
731
|
+
function loop(timestamp) {
|
|
732
|
+
frame = requestAnimationFrame(loop);
|
|
733
|
+
running = true;
|
|
734
|
+
repeat = 0;
|
|
735
|
+
// Run effects
|
|
736
|
+
flushGlobalEffects('before', timestamp);
|
|
737
|
+
// Render all roots
|
|
738
|
+
for (const root of roots.values()) {
|
|
739
|
+
const state = root.get();
|
|
740
|
+
// If the frameloop is invalidated, do not run another frame
|
|
741
|
+
if (state.internal.active &&
|
|
742
|
+
(state.frameloop === 'always' || state.internal.frames > 0) &&
|
|
743
|
+
!state.gl.xr?.isPresenting) {
|
|
744
|
+
repeat += render(timestamp, root);
|
|
408
745
|
}
|
|
409
746
|
}
|
|
410
|
-
//
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
747
|
+
// Run after-effects
|
|
748
|
+
flushGlobalEffects('after', timestamp);
|
|
749
|
+
// Stop the loop if nothing invalidates it
|
|
750
|
+
if (repeat === 0) {
|
|
751
|
+
// Tail call effects, they are called when rendering stops
|
|
752
|
+
flushGlobalEffects('tail', timestamp);
|
|
753
|
+
// Flag end of operation
|
|
754
|
+
running = false;
|
|
755
|
+
return cancelAnimationFrame(frame);
|
|
417
756
|
}
|
|
418
|
-
return intersections;
|
|
419
757
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
if (
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
const captureData = { intersection: hit, target: event.target };
|
|
433
|
-
if (internal.capturedMap.has(id)) {
|
|
434
|
-
// if the pointerId was previously captured, we add the hit to the event capturedMap
|
|
435
|
-
internal.capturedMap.get(id).set(hit.eventObject, captureData);
|
|
436
|
-
}
|
|
437
|
-
else {
|
|
438
|
-
// if the pointerId was not previously captured, we create a Map containing the hitObject, and the hit. hitObject is used for faster access
|
|
439
|
-
internal.capturedMap.set(id, new Map([[hit.eventObject, captureData]]));
|
|
440
|
-
}
|
|
441
|
-
// call the original event now
|
|
442
|
-
event.target.setPointerCapture(id);
|
|
443
|
-
};
|
|
444
|
-
const releasePointerCapture = (id) => {
|
|
445
|
-
const captures = internal.capturedMap.get(id);
|
|
446
|
-
if (captures) {
|
|
447
|
-
releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
|
|
448
|
-
}
|
|
449
|
-
};
|
|
450
|
-
// add native event props
|
|
451
|
-
const extractEventProps = {};
|
|
452
|
-
// 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.
|
|
453
|
-
for (const prop in event) {
|
|
454
|
-
const property = event[prop];
|
|
455
|
-
// only copy over atomics, leave functions alone as these should be called as event.nativeEvent.fn()
|
|
456
|
-
if (typeof property !== 'function') {
|
|
457
|
-
extractEventProps[prop] = property;
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
const raycastEvent = {
|
|
461
|
-
...hit,
|
|
462
|
-
...extractEventProps,
|
|
463
|
-
pointer,
|
|
464
|
-
intersections,
|
|
465
|
-
stopped: localState.stopped,
|
|
466
|
-
delta,
|
|
467
|
-
unprojectedPoint,
|
|
468
|
-
ray: raycaster.ray,
|
|
469
|
-
camera: camera,
|
|
470
|
-
// Hijack stopPropagation, which just sets a flag
|
|
471
|
-
stopPropagation() {
|
|
472
|
-
// https://github.com/pmndrs/react-three-fiber/issues/596
|
|
473
|
-
// Events are not allowed to stop propagation if the pointer has been captured
|
|
474
|
-
const capturesForPointer = 'pointerId' in event && internal.capturedMap.get(event.pointerId);
|
|
475
|
-
// We only authorize stopPropagation...
|
|
476
|
-
if (
|
|
477
|
-
// ...if this pointer hasn't been captured
|
|
478
|
-
!capturesForPointer ||
|
|
479
|
-
// ... or if the hit object is capturing the pointer
|
|
480
|
-
capturesForPointer.has(hit.eventObject)) {
|
|
481
|
-
raycastEvent.stopped = localState.stopped = true;
|
|
482
|
-
// Propagation is stopped, remove all other hover records
|
|
483
|
-
// An event handler is only allowed to flush other handlers if it is hovered itself
|
|
484
|
-
if (internal.hovered.size &&
|
|
485
|
-
Array.from(internal.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
|
|
486
|
-
// Objects cannot flush out higher up objects that have already caught the event
|
|
487
|
-
const higher = intersections.slice(0, intersections.indexOf(hit));
|
|
488
|
-
cancelPointer([...higher, hit]);
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
},
|
|
492
|
-
// there should be a distinction between target and currentTarget
|
|
493
|
-
target: { hasPointerCapture, setPointerCapture, releasePointerCapture },
|
|
494
|
-
currentTarget: { hasPointerCapture, setPointerCapture, releasePointerCapture },
|
|
495
|
-
nativeEvent: event,
|
|
496
|
-
};
|
|
497
|
-
// call subscribers
|
|
498
|
-
callback(raycastEvent);
|
|
499
|
-
// event bubbling may be interupted by stopPropagation
|
|
500
|
-
if (localState.stopped === true)
|
|
501
|
-
break;
|
|
502
|
-
}
|
|
758
|
+
function invalidate(store, frames = 1) {
|
|
759
|
+
const state = store?.get();
|
|
760
|
+
if (!state)
|
|
761
|
+
return roots.forEach((root) => invalidate(root, frames));
|
|
762
|
+
if (state.gl.xr?.isPresenting || !state.internal.active || state.frameloop === 'never')
|
|
763
|
+
return;
|
|
764
|
+
// Increase frames, do not go higher than 60
|
|
765
|
+
state.internal.frames = Math.min(60, state.internal.frames + frames);
|
|
766
|
+
// If the render-loop isn't active, start it
|
|
767
|
+
if (!running) {
|
|
768
|
+
running = true;
|
|
769
|
+
requestAnimationFrame(loop);
|
|
503
770
|
}
|
|
504
|
-
return intersections;
|
|
505
771
|
}
|
|
506
|
-
function
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
772
|
+
function advance(timestamp, runGlobalEffects = true, store, frame) {
|
|
773
|
+
if (runGlobalEffects)
|
|
774
|
+
flushGlobalEffects('before', timestamp);
|
|
775
|
+
const state = store?.get();
|
|
776
|
+
if (!state)
|
|
777
|
+
for (const root of roots.values())
|
|
778
|
+
render(timestamp, root);
|
|
779
|
+
else
|
|
780
|
+
render(timestamp, store, frame);
|
|
781
|
+
if (runGlobalEffects)
|
|
782
|
+
flushGlobalEffects('after', timestamp);
|
|
783
|
+
}
|
|
784
|
+
return {
|
|
785
|
+
loop,
|
|
786
|
+
/**
|
|
787
|
+
* Invalidates the view, requesting a frame to be rendered. Will globally invalidate unless passed a root's state.
|
|
788
|
+
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#invalidate
|
|
789
|
+
*/
|
|
790
|
+
invalidate,
|
|
791
|
+
/**
|
|
792
|
+
* Advances the frameloop and runs render effects, useful for when manually rendering via `frameloop="never"`.
|
|
793
|
+
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#advance
|
|
794
|
+
*/
|
|
795
|
+
advance,
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
const [injectNgtLoop, , NGT_LOOP] = createInjectionToken(() => createLoop(roots));
|
|
799
|
+
|
|
800
|
+
function safeDetectChanges(...cdrs) {
|
|
801
|
+
cdrs.forEach((cdr) => {
|
|
802
|
+
if (!cdr)
|
|
803
|
+
return;
|
|
804
|
+
try {
|
|
805
|
+
// dynamic created component with ViewContainerRef#createComponent does not have Context
|
|
806
|
+
// but it has _attachedToViewContainer
|
|
807
|
+
if (cdr['_attachedToViewContainer'] || !!cdr['context']) {
|
|
808
|
+
cdr.detectChanges();
|
|
525
809
|
}
|
|
526
810
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
for (let i = 0; i < objects.length; i++) {
|
|
530
|
-
const instance = getLocalState(objects[i]);
|
|
531
|
-
instance?.handlers.pointermissed?.(event);
|
|
811
|
+
catch (e) {
|
|
812
|
+
cdr.markForCheck();
|
|
532
813
|
}
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
const { internal } = store.get();
|
|
543
|
-
if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {
|
|
544
|
-
// If the object event interface had onLostPointerCapture, we'd call it here on every
|
|
545
|
-
// object that's getting removed.
|
|
546
|
-
internal.capturedMap.delete(event.pointerId);
|
|
547
|
-
cancelPointer([]);
|
|
548
|
-
}
|
|
549
|
-
};
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
function storeFactory(loop, document, injector, parent) {
|
|
818
|
+
return runInInjectionContext(injector, () => {
|
|
819
|
+
const window = document.defaultView;
|
|
820
|
+
if (!window) {
|
|
821
|
+
// TODO: revisit this when we need to support multiple platforms
|
|
822
|
+
throw new Error(`[NGT] Window is not available.`);
|
|
550
823
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
const
|
|
558
|
-
const
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
// Take care of unhover
|
|
578
|
-
if (isPointerMove)
|
|
579
|
-
cancelPointer(hits);
|
|
580
|
-
function onIntersect(data) {
|
|
581
|
-
const eventObject = data.eventObject;
|
|
582
|
-
const instance = getLocalState(eventObject);
|
|
583
|
-
const handlers = instance?.handlers;
|
|
584
|
-
// Check presence of handlers
|
|
585
|
-
if (!instance?.eventCount)
|
|
586
|
-
return;
|
|
587
|
-
if (isPointerMove) {
|
|
588
|
-
// Move event ...
|
|
589
|
-
if (handlers?.pointerover ||
|
|
590
|
-
handlers?.pointerenter ||
|
|
591
|
-
handlers?.pointerout ||
|
|
592
|
-
handlers?.pointerleave) {
|
|
593
|
-
// When enter or out is present take care of hover-state
|
|
594
|
-
const id = makeId(data);
|
|
595
|
-
const hoveredItem = internal.hovered.get(id);
|
|
596
|
-
if (!hoveredItem) {
|
|
597
|
-
// If the object wasn't previously hovered, book it and call its handler
|
|
598
|
-
internal.hovered.set(id, data);
|
|
599
|
-
handlers.pointerover?.(data);
|
|
600
|
-
handlers.pointerenter?.(data);
|
|
601
|
-
}
|
|
602
|
-
else if (hoveredItem.stopped) {
|
|
603
|
-
// If the object was previously hovered and stopped, we shouldn't allow other items to proceed
|
|
604
|
-
data.stopPropagation();
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
// Call mouse move
|
|
608
|
-
handlers?.pointermove?.(data);
|
|
824
|
+
const cdr = inject(ChangeDetectorRef);
|
|
825
|
+
// NOTE: using Subject because we do not care about late-subscribers
|
|
826
|
+
const pointerMissed$ = new Subject();
|
|
827
|
+
const store = signalStore(({ get, set }) => {
|
|
828
|
+
const { invalidate, advance } = loop;
|
|
829
|
+
const position = new THREE.Vector3();
|
|
830
|
+
const defaultTarget = new THREE.Vector3();
|
|
831
|
+
const tempTarget = new THREE.Vector3();
|
|
832
|
+
function getCurrentViewport(camera = get('camera'), target = defaultTarget, size = get('size')) {
|
|
833
|
+
const { width, height, top, left } = size;
|
|
834
|
+
const aspect = width / height;
|
|
835
|
+
if (target instanceof THREE.Vector3)
|
|
836
|
+
tempTarget.copy(target);
|
|
837
|
+
else
|
|
838
|
+
tempTarget.set(...target);
|
|
839
|
+
const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
|
|
840
|
+
if (is.orthographicCamera(camera)) {
|
|
841
|
+
return {
|
|
842
|
+
width: width / camera.zoom,
|
|
843
|
+
height: height / camera.zoom,
|
|
844
|
+
top,
|
|
845
|
+
left,
|
|
846
|
+
factor: 1,
|
|
847
|
+
distance,
|
|
848
|
+
aspect,
|
|
849
|
+
};
|
|
609
850
|
}
|
|
610
851
|
else {
|
|
611
|
-
//
|
|
612
|
-
const
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
// which must use the initial target
|
|
616
|
-
if (!isClickEvent || internal.initialHits.includes(eventObject)) {
|
|
617
|
-
// Missed events have to come first
|
|
618
|
-
pointerMissed(event, internal.interaction.filter((object) => !internal.initialHits.includes(object)));
|
|
619
|
-
// Now call the handler
|
|
620
|
-
handler(data);
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
else {
|
|
624
|
-
// Trigger onPointerMissed on all elements that have pointer over/out handlers, but not click and weren't hit
|
|
625
|
-
if (isClickEvent && internal.initialHits.includes(eventObject)) {
|
|
626
|
-
pointerMissed(event, internal.interaction.filter((object) => !internal.initialHits.includes(object)));
|
|
627
|
-
}
|
|
628
|
-
}
|
|
852
|
+
const fov = (camera.fov * Math.PI) / 180; // convert vertical fov to radians
|
|
853
|
+
const h = 2 * Math.tan(fov / 2) * distance; // visible height
|
|
854
|
+
const w = h * (width / height);
|
|
855
|
+
return { width: w, height: h, top, left, factor: width / w, distance, aspect };
|
|
629
856
|
}
|
|
630
857
|
}
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
858
|
+
let performanceTimeout = undefined;
|
|
859
|
+
const setPerformanceCurrent = (current) => set((state) => ({ performance: { ...state.performance, current } }));
|
|
860
|
+
const pointer = new THREE.Vector2();
|
|
861
|
+
return {
|
|
862
|
+
pointerMissed$: pointerMissed$.asObservable(),
|
|
863
|
+
events: { priority: 1, enabled: true, connected: false },
|
|
864
|
+
invalidate: (frames = 1) => invalidate(store, frames),
|
|
865
|
+
advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, store),
|
|
866
|
+
legacy: false,
|
|
867
|
+
linear: false,
|
|
868
|
+
flat: false,
|
|
869
|
+
controls: null,
|
|
870
|
+
clock: new THREE.Clock(),
|
|
871
|
+
pointer,
|
|
872
|
+
frameloop: 'always',
|
|
873
|
+
performance: {
|
|
874
|
+
current: 1,
|
|
875
|
+
min: 0.5,
|
|
876
|
+
max: 1,
|
|
877
|
+
debounce: 200,
|
|
878
|
+
regress: () => {
|
|
879
|
+
const state = get();
|
|
880
|
+
// Clear timeout
|
|
881
|
+
if (performanceTimeout)
|
|
882
|
+
clearTimeout(performanceTimeout);
|
|
883
|
+
// Set lower bound performance
|
|
884
|
+
if (state.performance.current !== state.performance.min)
|
|
885
|
+
setPerformanceCurrent(state.performance.min);
|
|
886
|
+
// Go back to upper bound performance after a while unless something regresses meanwhile
|
|
887
|
+
performanceTimeout = setTimeout(() => {
|
|
888
|
+
setPerformanceCurrent(get('performance', 'max'));
|
|
889
|
+
safeDetectChanges(cdr);
|
|
890
|
+
}, state.performance.debounce);
|
|
891
|
+
},
|
|
892
|
+
},
|
|
893
|
+
size: { width: 0, height: 0, top: 0, left: 0, updateStyle: false },
|
|
894
|
+
viewport: {
|
|
895
|
+
initialDpr: 0,
|
|
896
|
+
dpr: 0,
|
|
897
|
+
width: 0,
|
|
898
|
+
height: 0,
|
|
899
|
+
top: 0,
|
|
900
|
+
left: 0,
|
|
901
|
+
aspect: 0,
|
|
902
|
+
distance: 0,
|
|
903
|
+
factor: 0,
|
|
904
|
+
getCurrentViewport,
|
|
905
|
+
},
|
|
906
|
+
setEvents: (events) => set((state) => ({ ...state, events: { ...state.events, ...events } })),
|
|
907
|
+
setSize: (width, height, top, left) => {
|
|
908
|
+
const camera = get('camera');
|
|
909
|
+
const size = { width, height, top: top || 0, left: left || 0 };
|
|
910
|
+
set((state) => ({
|
|
911
|
+
size,
|
|
912
|
+
viewport: { ...state.viewport, ...getCurrentViewport(camera, defaultTarget, size) },
|
|
913
|
+
}));
|
|
914
|
+
},
|
|
915
|
+
setDpr: (dpr) => set((state) => {
|
|
916
|
+
const resolved = makeDpr(dpr, window);
|
|
917
|
+
return {
|
|
918
|
+
viewport: {
|
|
919
|
+
...state.viewport,
|
|
920
|
+
dpr: resolved,
|
|
921
|
+
initialDpr: state.viewport.initialDpr || resolved,
|
|
922
|
+
},
|
|
923
|
+
};
|
|
924
|
+
}),
|
|
925
|
+
setFrameloop: (frameloop = 'always') => {
|
|
926
|
+
const clock = get('clock');
|
|
927
|
+
// if frameloop === "never" clock.elapsedTime is updated using advance(timestamp)
|
|
928
|
+
clock.stop();
|
|
929
|
+
clock.elapsedTime = 0;
|
|
930
|
+
if (frameloop !== 'never') {
|
|
931
|
+
clock.start();
|
|
932
|
+
clock.elapsedTime = 0;
|
|
696
933
|
}
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
if (Array.isArray(input))
|
|
756
|
-
return input;
|
|
757
|
-
if (typeof input === 'string')
|
|
758
|
-
return [input];
|
|
759
|
-
return Object.values(input);
|
|
760
|
-
});
|
|
761
|
-
return () => {
|
|
762
|
-
const urls = computedUrls();
|
|
763
|
-
const loaderConstructor = loaderConstructorFactory(urls);
|
|
764
|
-
const loader = new loaderConstructor();
|
|
765
|
-
if (extensions)
|
|
766
|
-
extensions(loader);
|
|
767
|
-
return urls.map((url) => new Promise((resolve, reject) => {
|
|
768
|
-
if (cached.has(url)) {
|
|
769
|
-
resolve(cached.get(url));
|
|
934
|
+
set(() => ({ frameloop }));
|
|
935
|
+
},
|
|
936
|
+
previousRoot: parent,
|
|
937
|
+
internal: {
|
|
938
|
+
active: false,
|
|
939
|
+
priority: 0,
|
|
940
|
+
frames: 0,
|
|
941
|
+
lastEvent: new ElementRef(null),
|
|
942
|
+
interaction: [],
|
|
943
|
+
hovered: new Map(),
|
|
944
|
+
subscribers: [],
|
|
945
|
+
initialClick: [0, 0],
|
|
946
|
+
initialHits: [],
|
|
947
|
+
capturedMap: new Map(),
|
|
948
|
+
subscribe: (callback, priority = 0, _store = store) => {
|
|
949
|
+
const internal = get('internal');
|
|
950
|
+
// If this subscription was given a priority, it takes rendering into its own hands
|
|
951
|
+
// For that reason we switch off automatic rendering and increase the manual flag
|
|
952
|
+
// As long as this flag is positive there can be no internal rendering at all
|
|
953
|
+
// because there could be multiple render subscriptions
|
|
954
|
+
internal.priority = internal.priority + (priority > 0 ? 1 : 0);
|
|
955
|
+
internal.subscribers.push({ callback, priority, store });
|
|
956
|
+
// Register subscriber and sort layers from lowest to highest, meaning,
|
|
957
|
+
// highest priority renders last (on top of the other frames)
|
|
958
|
+
internal.subscribers = internal.subscribers.sort((a, b) => (a.priority || 0) - (b.priority || 0));
|
|
959
|
+
return () => {
|
|
960
|
+
const internal = get('internal');
|
|
961
|
+
if (internal?.subscribers) {
|
|
962
|
+
// Decrease manual flag if this subscription had a priority
|
|
963
|
+
internal.priority = internal.priority - (priority > 0 ? 1 : 0);
|
|
964
|
+
// Remove subscriber from list
|
|
965
|
+
internal.subscribers = internal.subscribers.filter((s) => s.callback !== callback);
|
|
966
|
+
}
|
|
967
|
+
};
|
|
968
|
+
},
|
|
969
|
+
},
|
|
970
|
+
};
|
|
971
|
+
});
|
|
972
|
+
// NOTE: assign pointerMissed$ so we can use it in events
|
|
973
|
+
Object.defineProperty(store, 'pointerMissed$', { get: () => pointerMissed$ });
|
|
974
|
+
const state = store.get();
|
|
975
|
+
let oldSize = state.size;
|
|
976
|
+
let oldDpr = state.viewport.dpr;
|
|
977
|
+
let oldCamera = state.camera;
|
|
978
|
+
const _camera = store.select('camera');
|
|
979
|
+
const _size = store.select('size');
|
|
980
|
+
const _viewport = store.select('viewport');
|
|
981
|
+
effect(() => {
|
|
982
|
+
const [camera, size, viewport, gl] = [_camera(), _size(), _viewport(), store.get('gl')];
|
|
983
|
+
// Resize camera and renderer on changes to size and pixelratio
|
|
984
|
+
if (size !== oldSize || viewport.dpr !== oldDpr) {
|
|
985
|
+
oldSize = size;
|
|
986
|
+
oldDpr = viewport.dpr;
|
|
987
|
+
// Update camera & renderer
|
|
988
|
+
updateCamera(camera, size);
|
|
989
|
+
gl.setPixelRatio(viewport.dpr);
|
|
990
|
+
const updateStyle = typeof HTMLCanvasElement !== 'undefined' && gl.domElement instanceof HTMLCanvasElement;
|
|
991
|
+
gl.setSize(size.width, size.height, updateStyle);
|
|
770
992
|
}
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
993
|
+
// Update viewport once the camera changes
|
|
994
|
+
if (camera !== oldCamera) {
|
|
995
|
+
oldCamera = camera;
|
|
996
|
+
updateCamera(camera, size);
|
|
997
|
+
// Update viewport
|
|
998
|
+
store.set((state) => ({
|
|
999
|
+
viewport: { ...state.viewport, ...state.viewport.getCurrentViewport(camera) },
|
|
1000
|
+
}));
|
|
778
1001
|
}
|
|
779
|
-
}));
|
|
780
|
-
};
|
|
781
|
-
}
|
|
782
|
-
function injectNgtLoader(loaderConstructorFactory, inputs, { extensions, onProgress, injector, } = {}) {
|
|
783
|
-
injector = assertInjectionContext(injectNgtLoader, injector);
|
|
784
|
-
return runInInjectionContext(injector, () => {
|
|
785
|
-
const cdr = inject(ChangeDetectorRef);
|
|
786
|
-
const response = signal(null);
|
|
787
|
-
const effector = load(loaderConstructorFactory, inputs, { extensions, onProgress });
|
|
788
|
-
requestAnimationInInjectionContext(() => {
|
|
789
|
-
effect(() => {
|
|
790
|
-
const originalUrls = untracked(inputs);
|
|
791
|
-
Promise.all(effector())
|
|
792
|
-
.then((results) => {
|
|
793
|
-
if (Array.isArray(originalUrls))
|
|
794
|
-
return results;
|
|
795
|
-
if (typeof originalUrls === 'string')
|
|
796
|
-
return results[0];
|
|
797
|
-
const keys = Object.keys(originalUrls);
|
|
798
|
-
return keys.reduce((result, key) => {
|
|
799
|
-
result[key] = results[keys.indexOf(key)];
|
|
800
|
-
return result;
|
|
801
|
-
}, {});
|
|
802
|
-
})
|
|
803
|
-
.then((value) => {
|
|
804
|
-
response.set(value);
|
|
805
|
-
safeDetectChanges(cdr);
|
|
806
|
-
});
|
|
807
|
-
});
|
|
808
1002
|
});
|
|
809
|
-
return
|
|
1003
|
+
return store;
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
const NGT_STORE = new InjectionToken('NgtStore token');
|
|
1007
|
+
const [injectNgtStore, provideNgtStore] = createInjectionToken(storeFactory, {
|
|
1008
|
+
isRoot: false,
|
|
1009
|
+
deps: [NGT_LOOP, DOCUMENT, Injector, [new Optional(), new SkipSelf(), NGT_STORE]],
|
|
1010
|
+
token: NGT_STORE,
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
function injectBeforeRender(cb, { priority = 0, injector } = {}) {
|
|
1014
|
+
injector = assertInjector(injectBeforeRender, injector);
|
|
1015
|
+
return runInInjectionContext(injector, () => {
|
|
1016
|
+
const store = injectNgtStore();
|
|
1017
|
+
const sub = store.get('internal').subscribe(cb, priority, store);
|
|
1018
|
+
inject(DestroyRef).onDestroy(() => void sub());
|
|
1019
|
+
return sub;
|
|
810
1020
|
});
|
|
811
1021
|
}
|
|
812
|
-
injectNgtLoader['preload'] = (loaderConstructorFactory, inputs, extensions) => {
|
|
813
|
-
Promise.all(load(loaderConstructorFactory, inputs, { extensions })());
|
|
814
|
-
};
|
|
815
|
-
injectNgtLoader['destroy'] = () => {
|
|
816
|
-
cached.clear();
|
|
817
|
-
};
|
|
818
1022
|
|
|
819
|
-
function createSubs(callback, subs) {
|
|
820
|
-
const sub = { callback };
|
|
821
|
-
subs.add(sub);
|
|
822
|
-
return () => void subs.delete(sub);
|
|
823
|
-
}
|
|
824
|
-
const globalEffects = new Set();
|
|
825
|
-
const globalAfterEffects = new Set();
|
|
826
|
-
const globalTailEffects = new Set();
|
|
827
|
-
/**
|
|
828
|
-
* Adds a global render callback which is called each frame.
|
|
829
|
-
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addEffect
|
|
830
|
-
*/
|
|
831
|
-
const addEffect = (callback) => createSubs(callback, globalEffects);
|
|
832
|
-
/**
|
|
833
|
-
* Adds a global after-render callback which is called each frame.
|
|
834
|
-
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addAfterEffect
|
|
835
|
-
*/
|
|
836
|
-
const addAfterEffect = (callback) => createSubs(callback, globalAfterEffects);
|
|
837
1023
|
/**
|
|
838
|
-
*
|
|
839
|
-
*
|
|
1024
|
+
* Release pointer captures.
|
|
1025
|
+
* This is called by releasePointerCapture in the API, and when an object is removed.
|
|
840
1026
|
*/
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
if (
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
1027
|
+
function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
|
|
1028
|
+
const captureData = captures.get(obj);
|
|
1029
|
+
if (captureData) {
|
|
1030
|
+
captures.delete(obj);
|
|
1031
|
+
// If this was the last capturing object for this pointer
|
|
1032
|
+
if (captures.size === 0) {
|
|
1033
|
+
capturedMap.delete(pointerId);
|
|
1034
|
+
captureData.target.releasePointerCapture(pointerId);
|
|
1035
|
+
}
|
|
847
1036
|
}
|
|
848
1037
|
}
|
|
849
|
-
function
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
1038
|
+
function removeInteractivity(store, object) {
|
|
1039
|
+
const { internal } = store.get();
|
|
1040
|
+
// Removes every trace of an object from the data store
|
|
1041
|
+
internal.interaction = internal.interaction.filter((o) => o !== object);
|
|
1042
|
+
internal.initialHits = internal.initialHits.filter((o) => o !== object);
|
|
1043
|
+
internal.hovered.forEach((value, key) => {
|
|
1044
|
+
if (value.eventObject === object || value.object === object) {
|
|
1045
|
+
// Clear out intersects, they are outdated by now
|
|
1046
|
+
internal.hovered.delete(key);
|
|
1047
|
+
}
|
|
1048
|
+
});
|
|
1049
|
+
internal.capturedMap.forEach((captures, pointerId) => {
|
|
1050
|
+
releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
|
|
1051
|
+
});
|
|
858
1052
|
}
|
|
859
|
-
function
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
state.clock.oldTime = state.clock.elapsedTime;
|
|
867
|
-
state.clock.elapsedTime = timestamp;
|
|
1053
|
+
function createEvents(store) {
|
|
1054
|
+
/** Calculates delta */
|
|
1055
|
+
function calculateDistance(event) {
|
|
1056
|
+
const internal = store.get('internal');
|
|
1057
|
+
const dx = event.offsetX - internal.initialClick[0];
|
|
1058
|
+
const dy = event.offsetY - internal.initialClick[1];
|
|
1059
|
+
return Math.round(Math.sqrt(dx * dx + dy * dy));
|
|
868
1060
|
}
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
1061
|
+
/** Returns true if an instance has a valid pointer-event registered, this excludes scroll, clicks etc */
|
|
1062
|
+
function filterPointerEvents(objects) {
|
|
1063
|
+
return objects.filter((obj) => ['move', 'over', 'enter', 'out', 'leave'].some((name) => {
|
|
1064
|
+
const eventName = `pointer${name}`;
|
|
1065
|
+
return getLocalState(obj).handlers?.[eventName];
|
|
1066
|
+
}));
|
|
874
1067
|
}
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
function loop(timestamp) {
|
|
887
|
-
frame = requestAnimationFrame(loop);
|
|
888
|
-
running = true;
|
|
889
|
-
repeat = 0;
|
|
890
|
-
// Run effects
|
|
891
|
-
flushGlobalEffects('before', timestamp);
|
|
892
|
-
// Render all roots
|
|
893
|
-
for (const root of roots.values()) {
|
|
894
|
-
const state = root.get();
|
|
895
|
-
// If the frameloop is invalidated, do not run another frame
|
|
896
|
-
if (state.internal.active &&
|
|
897
|
-
(state.frameloop === 'always' || state.internal.frames > 0) &&
|
|
898
|
-
!state.gl.xr?.isPresenting) {
|
|
899
|
-
repeat += render(timestamp, root);
|
|
1068
|
+
function intersect(event, filter) {
|
|
1069
|
+
const state = store.get();
|
|
1070
|
+
const duplicates = new Set();
|
|
1071
|
+
const intersections = [];
|
|
1072
|
+
// Allow callers to eliminate event objects
|
|
1073
|
+
const eventsObjects = filter ? filter(state.internal.interaction) : state.internal.interaction;
|
|
1074
|
+
// Reset all raycaster cameras to undefined
|
|
1075
|
+
for (let i = 0; i < eventsObjects.length; i++) {
|
|
1076
|
+
const state = getLocalState(eventsObjects[i]).store.get();
|
|
1077
|
+
if (state) {
|
|
1078
|
+
state.raycaster.camera = undefined;
|
|
900
1079
|
}
|
|
901
1080
|
}
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
if (repeat === 0) {
|
|
906
|
-
// Tail call effects, they are called when rendering stops
|
|
907
|
-
flushGlobalEffects('tail', timestamp);
|
|
908
|
-
// Flag end of operation
|
|
909
|
-
running = false;
|
|
910
|
-
return cancelAnimationFrame(frame);
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
function invalidate(store, frames = 1) {
|
|
914
|
-
const state = store?.get();
|
|
915
|
-
if (!state)
|
|
916
|
-
return roots.forEach((root) => invalidate(root, frames));
|
|
917
|
-
if (state.gl.xr?.isPresenting || !state.internal.active || state.frameloop === 'never')
|
|
918
|
-
return;
|
|
919
|
-
// Increase frames, do not go higher than 60
|
|
920
|
-
state.internal.frames = Math.min(60, state.internal.frames + frames);
|
|
921
|
-
// If the render-loop isn't active, start it
|
|
922
|
-
if (!running) {
|
|
923
|
-
running = true;
|
|
924
|
-
requestAnimationFrame(loop);
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
function advance(timestamp, runGlobalEffects = true, store, frame) {
|
|
928
|
-
const state = store?.get();
|
|
929
|
-
if (runGlobalEffects)
|
|
930
|
-
flushGlobalEffects('before', timestamp);
|
|
931
|
-
if (!state)
|
|
932
|
-
for (const root of roots.values())
|
|
933
|
-
render(timestamp, root);
|
|
934
|
-
else
|
|
935
|
-
render(timestamp, store, frame);
|
|
936
|
-
if (runGlobalEffects)
|
|
937
|
-
flushGlobalEffects('after', timestamp);
|
|
938
|
-
}
|
|
939
|
-
return {
|
|
940
|
-
loop,
|
|
941
|
-
/**
|
|
942
|
-
* Invalidates the view, requesting a frame to be rendered. Will globally invalidate unless passed a root's state.
|
|
943
|
-
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#invalidate
|
|
944
|
-
*/
|
|
945
|
-
invalidate,
|
|
946
|
-
/**
|
|
947
|
-
* Advances the frameloop and runs render effects, useful for when manually rendering via `frameloop="never"`.
|
|
948
|
-
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#advance
|
|
949
|
-
*/
|
|
950
|
-
advance,
|
|
951
|
-
};
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
function diffProps(instance, props) {
|
|
955
|
-
const propsEntries = Object.entries(props);
|
|
956
|
-
const changes = [];
|
|
957
|
-
for (const [propKey, propValue] of propsEntries) {
|
|
958
|
-
if (is.equ(propValue, instance[propKey]))
|
|
959
|
-
continue;
|
|
960
|
-
changes.push([propKey, propValue]);
|
|
961
|
-
}
|
|
962
|
-
return changes;
|
|
963
|
-
}
|
|
964
|
-
function applyProps(instance, props) {
|
|
965
|
-
// if props is empty
|
|
966
|
-
if (!Object.keys(props).length)
|
|
967
|
-
return instance;
|
|
968
|
-
// filter equals, events , and reserved props
|
|
969
|
-
const localState = getLocalState(instance);
|
|
970
|
-
const rootState = localState.store?.get();
|
|
971
|
-
const changes = diffProps(instance, props);
|
|
972
|
-
for (let i = 0; i < changes.length; i++) {
|
|
973
|
-
let key = changes[i][0];
|
|
974
|
-
const currentInstance = instance;
|
|
975
|
-
const targetProp = currentInstance[key];
|
|
976
|
-
let value = changes[i][1];
|
|
977
|
-
if (is.colorSpaceExist(currentInstance)) {
|
|
978
|
-
const sRGBEncoding = 3001;
|
|
979
|
-
const SRGBColorSpace = 'srgb';
|
|
980
|
-
const LinearSRGBColorSpace = 'srgb-linear';
|
|
981
|
-
if (key === 'encoding') {
|
|
982
|
-
key = 'colorSpace';
|
|
983
|
-
value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
|
|
984
|
-
}
|
|
985
|
-
else if (key === 'outputEncoding') {
|
|
986
|
-
key = 'outputColorSpace';
|
|
987
|
-
value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
|
|
988
|
-
}
|
|
1081
|
+
if (!state.previousRoot) {
|
|
1082
|
+
// Make sure root-level pointer and ray are set up
|
|
1083
|
+
state.events.compute?.(event, store, null);
|
|
989
1084
|
}
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
const
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
value.constructor &&
|
|
1004
|
-
targetProp.constructor.name === value.constructor.name) {
|
|
1005
|
-
targetProp['copy'](value);
|
|
1006
|
-
if (!THREE.ColorManagement && !rootState.linear && isColor)
|
|
1007
|
-
targetProp['convertSRGBToLinear']();
|
|
1008
|
-
}
|
|
1009
|
-
// if nothing else fits, just set the single value, ignore undefined
|
|
1010
|
-
else if (value !== undefined) {
|
|
1011
|
-
const isColor = targetProp instanceof THREE.Color;
|
|
1012
|
-
// allow setting array scalars
|
|
1013
|
-
if (!isColor && targetProp['setScalar'])
|
|
1014
|
-
targetProp['setScalar'](value);
|
|
1015
|
-
// layers have no copy function, copy the mask
|
|
1016
|
-
else if (targetProp instanceof THREE.Layers && value instanceof THREE.Layers)
|
|
1017
|
-
targetProp.mask = value.mask;
|
|
1018
|
-
// otherwise just set ...
|
|
1019
|
-
else
|
|
1020
|
-
targetProp['set'](value);
|
|
1021
|
-
// auto-convert srgb
|
|
1022
|
-
if (!THREE.ColorManagement && !rootState?.linear && isColor)
|
|
1023
|
-
targetProp.convertSRGBToLinear();
|
|
1085
|
+
function handleRaycast(obj) {
|
|
1086
|
+
const objLocalState = getLocalState(obj);
|
|
1087
|
+
const objStore = objLocalState.store;
|
|
1088
|
+
const objState = objStore?.get();
|
|
1089
|
+
// Skip event handling when noEvents is set, or when the raycasters camera is null
|
|
1090
|
+
if (!objState || !objState.events.enabled || objState.raycaster.camera === null)
|
|
1091
|
+
return [];
|
|
1092
|
+
// When the camera is undefined we have to call the event layers update function
|
|
1093
|
+
if (objState.raycaster.camera === undefined) {
|
|
1094
|
+
objState.events.compute?.(event, objStore, objState.previousRoot);
|
|
1095
|
+
// If the camera is still undefined we have to skip this layer entirely
|
|
1096
|
+
if (objState.raycaster.camera === undefined)
|
|
1097
|
+
objState.raycaster.camera = null;
|
|
1024
1098
|
}
|
|
1025
|
-
|
|
1099
|
+
// Intersect object by object
|
|
1100
|
+
return objState.raycaster.camera ? objState.raycaster.intersectObject(obj, true) : [];
|
|
1026
1101
|
}
|
|
1027
|
-
//
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1102
|
+
// Collect events
|
|
1103
|
+
let hits = eventsObjects
|
|
1104
|
+
// Intersect objects
|
|
1105
|
+
.flatMap(handleRaycast)
|
|
1106
|
+
// Sort by event priority and distance
|
|
1107
|
+
.sort((a, b) => {
|
|
1108
|
+
const aState = getLocalState(a.object).store?.get();
|
|
1109
|
+
const bState = getLocalState(b.object).store?.get();
|
|
1110
|
+
if (!aState || !bState)
|
|
1111
|
+
return a.distance - b.distance;
|
|
1112
|
+
return bState.events.priority - aState.events.priority || a.distance - b.distance;
|
|
1113
|
+
})
|
|
1114
|
+
// Filter out duplicates
|
|
1115
|
+
.filter((item) => {
|
|
1116
|
+
const id = makeId(item);
|
|
1117
|
+
if (duplicates.has(id))
|
|
1118
|
+
return false;
|
|
1119
|
+
duplicates.add(id);
|
|
1120
|
+
return true;
|
|
1121
|
+
});
|
|
1122
|
+
// https://github.com/mrdoob/three.js/issues/16031
|
|
1123
|
+
// Allow custom userland intersect sort order, this likely only makes sense on the root filter
|
|
1124
|
+
if (state.events.filter)
|
|
1125
|
+
hits = state.events.filter(hits, store);
|
|
1126
|
+
// Bubble up the events, find the event source (eventObject)
|
|
1127
|
+
for (const hit of hits) {
|
|
1128
|
+
let eventObject = hit.object;
|
|
1129
|
+
// bubble event up
|
|
1130
|
+
while (eventObject) {
|
|
1131
|
+
if (getLocalState(eventObject).eventCount) {
|
|
1132
|
+
intersections.push({ ...hit, eventObject });
|
|
1040
1133
|
}
|
|
1134
|
+
eventObject = eventObject.parent;
|
|
1041
1135
|
}
|
|
1042
|
-
localState?.nativeProps?.set({ [key]: value });
|
|
1043
1136
|
}
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
if (index > -1)
|
|
1053
|
-
rootState.internal.interaction.splice(index, 1);
|
|
1054
|
-
// Add the instance to the interaction manager only when it has handlers
|
|
1055
|
-
if (localState.eventCount)
|
|
1056
|
-
rootState.internal.interaction.push(instance);
|
|
1057
|
-
}
|
|
1058
|
-
if (parent && localState.afterUpdate && localState.afterUpdate.observed && changes.length) {
|
|
1059
|
-
localState.afterUpdate.emit(instance);
|
|
1137
|
+
// If the interaction is captured, make all capturing targets part of the intersect.
|
|
1138
|
+
if ('pointerId' in event && state.internal.capturedMap.has(event.pointerId)) {
|
|
1139
|
+
for (let captureData of state.internal.capturedMap.get(event.pointerId).values()) {
|
|
1140
|
+
if (!duplicates.has(makeId(captureData.intersection)))
|
|
1141
|
+
intersections.push(captureData.intersection);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
return intersections;
|
|
1060
1145
|
}
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
const
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1146
|
+
/** Handles intersections by forwarding them to handlers */
|
|
1147
|
+
function handleIntersects(intersections, event, delta, callback) {
|
|
1148
|
+
const rootState = store.get();
|
|
1149
|
+
// If anything has been found, forward it to the event listeners
|
|
1150
|
+
if (intersections.length) {
|
|
1151
|
+
const localState = { stopped: false };
|
|
1152
|
+
for (const hit of intersections) {
|
|
1153
|
+
const state = getLocalState(hit.object).store?.get() || rootState;
|
|
1154
|
+
const { raycaster, pointer, camera, internal } = state;
|
|
1155
|
+
const unprojectedPoint = new THREE.Vector3(pointer.x, pointer.y, 0).unproject(camera);
|
|
1156
|
+
const hasPointerCapture = (id) => internal.capturedMap.get(id)?.has(hit.eventObject) ?? false;
|
|
1157
|
+
const setPointerCapture = (id) => {
|
|
1158
|
+
const captureData = { intersection: hit, target: event.target };
|
|
1159
|
+
if (internal.capturedMap.has(id)) {
|
|
1160
|
+
// if the pointerId was previously captured, we add the hit to the
|
|
1161
|
+
// event capturedMap.
|
|
1162
|
+
internal.capturedMap.get(id).set(hit.eventObject, captureData);
|
|
1163
|
+
}
|
|
1164
|
+
else {
|
|
1165
|
+
// if the pointerId was not previously captured, we create a map
|
|
1166
|
+
// containing the hitObject, and the hit. hitObject is used for
|
|
1167
|
+
// faster access.
|
|
1168
|
+
internal.capturedMap.set(id, new Map([[hit.eventObject, captureData]]));
|
|
1169
|
+
}
|
|
1170
|
+
// Call the original event now
|
|
1171
|
+
event.target.setPointerCapture(id);
|
|
1172
|
+
};
|
|
1173
|
+
const releasePointerCapture = (id) => {
|
|
1174
|
+
const captures = internal.capturedMap.get(id);
|
|
1175
|
+
if (captures) {
|
|
1176
|
+
releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
|
|
1177
|
+
}
|
|
1178
|
+
};
|
|
1179
|
+
// Add native event props
|
|
1180
|
+
const extractEventProps = {};
|
|
1181
|
+
// 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.
|
|
1182
|
+
for (let prop in event) {
|
|
1183
|
+
let property = event[prop];
|
|
1184
|
+
// Only copy over atomics, leave functions alone as these should be
|
|
1185
|
+
// called as event.nativeEvent.fn()
|
|
1186
|
+
if (typeof property !== 'function')
|
|
1187
|
+
extractEventProps[prop] = property;
|
|
1101
1188
|
}
|
|
1102
|
-
const
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
const state = this.get();
|
|
1132
|
-
// clear timeout
|
|
1133
|
-
if (performanceTimeout)
|
|
1134
|
-
clearTimeout(performanceTimeout);
|
|
1135
|
-
// set lower bound
|
|
1136
|
-
if (state.performance.current !== state.performance.min)
|
|
1137
|
-
setPerformanceCurrent(state.performance.min);
|
|
1138
|
-
// go back to upper bound
|
|
1139
|
-
performanceTimeout = setTimeout(() => {
|
|
1140
|
-
setPerformanceCurrent(this.get('performance', 'max') || 1);
|
|
1141
|
-
}, state.performance.debounce);
|
|
1142
|
-
},
|
|
1143
|
-
},
|
|
1144
|
-
size: { width: 0, height: 0, top: 0, left: 0 },
|
|
1145
|
-
viewport: {
|
|
1146
|
-
initialDpr: 0,
|
|
1147
|
-
dpr: 0,
|
|
1148
|
-
width: 0,
|
|
1149
|
-
height: 0,
|
|
1150
|
-
top: 0,
|
|
1151
|
-
left: 0,
|
|
1152
|
-
aspect: 0,
|
|
1153
|
-
distance: 0,
|
|
1154
|
-
factor: 0,
|
|
1155
|
-
getCurrentViewport,
|
|
1156
|
-
},
|
|
1157
|
-
previousStore: this.#parentStore,
|
|
1158
|
-
internal: {
|
|
1159
|
-
active: false,
|
|
1160
|
-
priority: 0,
|
|
1161
|
-
frames: 0,
|
|
1162
|
-
lastEvent: new ElementRef(null),
|
|
1163
|
-
interaction: [],
|
|
1164
|
-
hovered: new Map(),
|
|
1165
|
-
subscribers: [],
|
|
1166
|
-
initialClick: [0, 0],
|
|
1167
|
-
initialHits: [],
|
|
1168
|
-
capturedMap: new Map(),
|
|
1169
|
-
subscribe: (callback, priority = 0, store = this) => {
|
|
1170
|
-
const internal = this.get('internal');
|
|
1171
|
-
// If this subscription was given a priority, it takes rendering into its own hands
|
|
1172
|
-
// For that reason we switch off automatic rendering and increase the manual flag
|
|
1173
|
-
// As long as this flag is positive there can be no internal rendering at all
|
|
1174
|
-
// because there could be multiple render subscriptions
|
|
1175
|
-
internal.priority = internal.priority + (priority > 0 ? 1 : 0);
|
|
1176
|
-
internal.subscribers.push({ priority, store, callback });
|
|
1177
|
-
// Register subscriber and sort layers from lowest to highest, meaning,
|
|
1178
|
-
// highest priority renders last (on top of the other frames)
|
|
1179
|
-
internal.subscribers.sort((a, b) => (a.priority || 0) - (b.priority || 0));
|
|
1180
|
-
return () => {
|
|
1181
|
-
const internal = this.get('internal');
|
|
1182
|
-
if (internal?.subscribers) {
|
|
1183
|
-
// Decrease manual flag if this subscription had a priority
|
|
1184
|
-
internal.priority = internal.priority - (priority > 0 ? 1 : 0);
|
|
1185
|
-
// Remove subscriber from list
|
|
1186
|
-
internal.subscribers = internal.subscribers.filter((s) => s.callback !== callback);
|
|
1189
|
+
const raycastEvent = {
|
|
1190
|
+
...hit,
|
|
1191
|
+
...extractEventProps,
|
|
1192
|
+
pointer,
|
|
1193
|
+
intersections,
|
|
1194
|
+
stopped: localState.stopped,
|
|
1195
|
+
delta,
|
|
1196
|
+
unprojectedPoint,
|
|
1197
|
+
ray: raycaster.ray,
|
|
1198
|
+
camera,
|
|
1199
|
+
// Hijack stopPropagation, which just sets a flag
|
|
1200
|
+
stopPropagation() {
|
|
1201
|
+
// https://github.com/pmndrs/react-three-fiber/issues/596
|
|
1202
|
+
// Events are not allowed to stop propagation if the pointer has been captured
|
|
1203
|
+
const capturesForPointer = 'pointerId' in event && internal.capturedMap.get(event.pointerId);
|
|
1204
|
+
// We only authorize stopPropagation...
|
|
1205
|
+
if (
|
|
1206
|
+
// ...if this pointer hasn't been captured
|
|
1207
|
+
!capturesForPointer ||
|
|
1208
|
+
// ... or if the hit object is capturing the pointer
|
|
1209
|
+
capturesForPointer.has(hit.eventObject)) {
|
|
1210
|
+
raycastEvent.stopped = localState.stopped = true;
|
|
1211
|
+
// Propagation is stopped, remove all other hover records
|
|
1212
|
+
// An event handler is only allowed to flush other handlers if it is hovered itself
|
|
1213
|
+
if (internal.hovered.size &&
|
|
1214
|
+
Array.from(internal.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
|
|
1215
|
+
// Objects cannot flush out higher up objects that have already caught the event
|
|
1216
|
+
const higher = intersections.slice(0, intersections.indexOf(hit));
|
|
1217
|
+
cancelPointer([...higher, hit]);
|
|
1187
1218
|
}
|
|
1188
|
-
}
|
|
1219
|
+
}
|
|
1189
1220
|
},
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
});
|
|
1223
|
-
this.isInit = true;
|
|
1224
|
-
this.#resize();
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1227
|
-
configure(inputs, canvasElement) {
|
|
1228
|
-
const { gl: glOptions, size: sizeOptions, camera: cameraOptions, raycaster: raycasterOptions, scene: sceneOptions, events, orthographic, lookAt, shadows, linear, legacy, flat, dpr, frameloop, performance, } = inputs;
|
|
1229
|
-
const state = this.get();
|
|
1230
|
-
const stateToUpdate = {};
|
|
1231
|
-
// setup renderer
|
|
1232
|
-
let gl = state.gl;
|
|
1233
|
-
if (!state.gl)
|
|
1234
|
-
stateToUpdate.gl = gl = makeDefaultRenderer(glOptions, canvasElement);
|
|
1235
|
-
// setup raycaster
|
|
1236
|
-
let raycaster = state.raycaster;
|
|
1237
|
-
if (!raycaster)
|
|
1238
|
-
stateToUpdate.raycaster = raycaster = new THREE.Raycaster();
|
|
1239
|
-
// set raycaster options
|
|
1240
|
-
const { params, ...options } = raycasterOptions || {};
|
|
1241
|
-
if (!is.equ(options, raycaster, shallowLoose))
|
|
1242
|
-
applyProps(raycaster, { ...options });
|
|
1243
|
-
if (!is.equ(params, raycaster.params, shallowLoose)) {
|
|
1244
|
-
applyProps(raycaster, { params: { ...raycaster.params, ...(params || {}) } });
|
|
1245
|
-
}
|
|
1246
|
-
// create default camera
|
|
1247
|
-
if (!state.camera) {
|
|
1248
|
-
const isCamera = is.camera(cameraOptions);
|
|
1249
|
-
let camera = isCamera ? cameraOptions : makeDefaultCamera(orthographic || false, state.size);
|
|
1250
|
-
if (!isCamera) {
|
|
1251
|
-
if (cameraOptions)
|
|
1252
|
-
applyProps(camera, cameraOptions);
|
|
1253
|
-
// set position.z
|
|
1254
|
-
if (!cameraOptions?.position)
|
|
1255
|
-
camera.position.z = 5;
|
|
1256
|
-
// always look at center or passed-in lookAt by default
|
|
1257
|
-
if (!cameraOptions?.rotation && !cameraOptions?.quaternion) {
|
|
1258
|
-
if (Array.isArray(lookAt))
|
|
1259
|
-
camera.lookAt(lookAt[0], lookAt[1], lookAt[2]);
|
|
1260
|
-
else if (lookAt instanceof THREE.Vector3)
|
|
1261
|
-
camera.lookAt(lookAt);
|
|
1262
|
-
else
|
|
1263
|
-
camera.lookAt(0, 0, 0);
|
|
1221
|
+
// there should be a distinction between target and currentTarget
|
|
1222
|
+
target: { hasPointerCapture, setPointerCapture, releasePointerCapture },
|
|
1223
|
+
currentTarget: { hasPointerCapture, setPointerCapture, releasePointerCapture },
|
|
1224
|
+
nativeEvent: event,
|
|
1225
|
+
};
|
|
1226
|
+
// Call subscribers
|
|
1227
|
+
callback(raycastEvent);
|
|
1228
|
+
// Event bubbling may be interrupted by stopPropagation
|
|
1229
|
+
if (localState.stopped === true)
|
|
1230
|
+
break;
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
return intersections;
|
|
1234
|
+
}
|
|
1235
|
+
function cancelPointer(intersections) {
|
|
1236
|
+
const internal = store.get('internal');
|
|
1237
|
+
for (const hoveredObj of internal.hovered.values()) {
|
|
1238
|
+
// When no objects were hit or the the hovered object wasn't found underneath the cursor
|
|
1239
|
+
// we call onPointerOut and delete the object from the hovered-elements map
|
|
1240
|
+
if (!intersections.length ||
|
|
1241
|
+
!intersections.find((hit) => hit.object === hoveredObj.object &&
|
|
1242
|
+
hit.index === hoveredObj.index &&
|
|
1243
|
+
hit.instanceId === hoveredObj.instanceId)) {
|
|
1244
|
+
const eventObject = hoveredObj.eventObject;
|
|
1245
|
+
const instance = getLocalState(eventObject);
|
|
1246
|
+
const handlers = instance?.handlers;
|
|
1247
|
+
internal.hovered.delete(makeId(hoveredObj));
|
|
1248
|
+
if (instance?.eventCount) {
|
|
1249
|
+
// Clear out intersects, they are outdated by now
|
|
1250
|
+
const data = { ...hoveredObj, intersections };
|
|
1251
|
+
handlers?.pointerout?.(data);
|
|
1252
|
+
handlers?.pointerleave?.(data);
|
|
1264
1253
|
}
|
|
1265
|
-
// update projection matrix after applyprops
|
|
1266
|
-
camera.updateProjectionMatrix?.();
|
|
1267
1254
|
}
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
function pointerMissed(event, objects) {
|
|
1258
|
+
for (let i = 0; i < objects.length; i++) {
|
|
1259
|
+
const instance = getLocalState(objects[i]);
|
|
1260
|
+
instance?.handlers.pointermissed?.(event);
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
function handlePointer(name) {
|
|
1264
|
+
// Deal with cancelation
|
|
1265
|
+
switch (name) {
|
|
1266
|
+
case 'pointerleave':
|
|
1267
|
+
case 'pointercancel':
|
|
1268
|
+
return () => cancelPointer([]);
|
|
1269
|
+
case 'lostpointercapture':
|
|
1270
|
+
return (event) => {
|
|
1271
|
+
const { internal } = store.get();
|
|
1272
|
+
if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {
|
|
1273
|
+
// If the object event interface had onLostPointerCapture, we'd call it here on every
|
|
1274
|
+
// object that's getting removed.
|
|
1275
|
+
internal.capturedMap.delete(event.pointerId);
|
|
1276
|
+
cancelPointer([]);
|
|
1277
|
+
}
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
// Any other pointer goes here ...
|
|
1281
|
+
return function handleEvent(event) {
|
|
1282
|
+
const pointerMissed$ = store['pointerMissed$'];
|
|
1283
|
+
const internal = store.get('internal');
|
|
1284
|
+
// prepareRay(event)
|
|
1285
|
+
internal.lastEvent.nativeElement = event;
|
|
1286
|
+
// Get fresh intersects
|
|
1287
|
+
const isPointerMove = name === 'pointermove';
|
|
1288
|
+
const isClickEvent = name === 'click' || name === 'contextmenu' || name === 'dblclick';
|
|
1289
|
+
const filter = isPointerMove ? filterPointerEvents : undefined;
|
|
1290
|
+
const hits = intersect(event, filter);
|
|
1291
|
+
const delta = isClickEvent ? calculateDistance(event) : 0;
|
|
1292
|
+
// Save initial coordinates on pointer-down
|
|
1293
|
+
if (name === 'pointerdown') {
|
|
1294
|
+
internal.initialClick = [event.offsetX, event.offsetY];
|
|
1295
|
+
internal.initialHits = hits.map((hit) => hit.eventObject);
|
|
1296
|
+
}
|
|
1297
|
+
// If a click yields no results, pass it back to the user as a miss
|
|
1298
|
+
// Missed events have to come first in order to establish user-land side-effect clean up
|
|
1299
|
+
if (isClickEvent && !hits.length) {
|
|
1300
|
+
if (delta <= 2) {
|
|
1301
|
+
pointerMissed(event, internal.interaction);
|
|
1302
|
+
pointerMissed$.next(event);
|
|
1303
|
+
}
|
|
1277
1304
|
}
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
// Handle frame behavior in WebXR
|
|
1288
|
-
const handleXRFrame = (timestamp, frame) => {
|
|
1289
|
-
const state = this.get();
|
|
1290
|
-
if (state.frameloop === 'never')
|
|
1305
|
+
// Take care of unhover
|
|
1306
|
+
if (isPointerMove)
|
|
1307
|
+
cancelPointer(hits);
|
|
1308
|
+
function onIntersect(data) {
|
|
1309
|
+
const eventObject = data.eventObject;
|
|
1310
|
+
const instance = getLocalState(eventObject);
|
|
1311
|
+
const handlers = instance?.handlers;
|
|
1312
|
+
// Check presence of handlers
|
|
1313
|
+
if (!instance?.eventCount)
|
|
1291
1314
|
return;
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
basic: THREE.BasicShadowMap,
|
|
1329
|
-
percentage: THREE.PCFShadowMap,
|
|
1330
|
-
soft: THREE.PCFSoftShadowMap,
|
|
1331
|
-
variance: THREE.VSMShadowMap,
|
|
1332
|
-
};
|
|
1333
|
-
gl.shadowMap.type = types[shadows] ?? THREE.PCFSoftShadowMap;
|
|
1334
|
-
}
|
|
1335
|
-
else if (is.obj(shadows)) {
|
|
1336
|
-
Object.assign(gl.shadowMap, shadows);
|
|
1337
|
-
}
|
|
1338
|
-
if (oldEnabled !== gl.shadowMap.enabled || oldType !== gl.shadowMap.type)
|
|
1339
|
-
checkNeedsUpdate(gl.shadowMap);
|
|
1340
|
-
}
|
|
1341
|
-
// Safely set color management if available.
|
|
1342
|
-
// Avoid accessing THREE.ColorManagement to play nice with older versions
|
|
1343
|
-
if (THREE.ColorManagement) {
|
|
1344
|
-
const ColorManagement = THREE.ColorManagement;
|
|
1345
|
-
if ('enabled' in ColorManagement)
|
|
1346
|
-
ColorManagement['enabled'] = !legacy ?? false;
|
|
1347
|
-
else if ('legacyMode' in ColorManagement)
|
|
1348
|
-
ColorManagement['legacyMode'] = legacy ?? true;
|
|
1349
|
-
}
|
|
1350
|
-
// set color space and tonemapping preferences
|
|
1351
|
-
const LinearEncoding = 3000;
|
|
1352
|
-
const sRGBEncoding = 3001;
|
|
1353
|
-
applyProps(gl, {
|
|
1354
|
-
outputEncoding: linear ? LinearEncoding : sRGBEncoding,
|
|
1355
|
-
toneMapping: flat ? THREE.NoToneMapping : THREE.ACESFilmicToneMapping,
|
|
1356
|
-
});
|
|
1357
|
-
// Update color management state
|
|
1358
|
-
if (state.legacy !== legacy)
|
|
1359
|
-
stateToUpdate.legacy = legacy;
|
|
1360
|
-
if (state.linear !== linear)
|
|
1361
|
-
stateToUpdate.linear = linear;
|
|
1362
|
-
if (state.flat !== flat)
|
|
1363
|
-
stateToUpdate.flat = flat;
|
|
1364
|
-
// Set gl props
|
|
1365
|
-
gl.setClearAlpha(0);
|
|
1366
|
-
gl.setPixelRatio(makeDpr(state.viewport.dpr));
|
|
1367
|
-
gl.setSize(state.size.width, state.size.height);
|
|
1368
|
-
if (is.obj(glOptions) &&
|
|
1369
|
-
!(typeof glOptions === 'function') &&
|
|
1370
|
-
!is.renderer(glOptions) &&
|
|
1371
|
-
!is.equ(glOptions, gl, shallowLoose)) {
|
|
1372
|
-
applyProps(gl, glOptions);
|
|
1373
|
-
}
|
|
1374
|
-
// Store events internally
|
|
1375
|
-
if (events && !state.events.handlers)
|
|
1376
|
-
stateToUpdate.events = events(this);
|
|
1377
|
-
// Check performance
|
|
1378
|
-
if (performance && !is.equ(performance, state.performance, shallowLoose)) {
|
|
1379
|
-
stateToUpdate.performance = { ...state.performance, ...performance };
|
|
1380
|
-
}
|
|
1381
|
-
this.set(stateToUpdate);
|
|
1382
|
-
// Check pixelratio
|
|
1383
|
-
if (dpr && state.viewport.dpr !== makeDpr(dpr))
|
|
1384
|
-
state.setDpr(dpr);
|
|
1385
|
-
// Check size, allow it to take on container bounds initially
|
|
1386
|
-
const size = computeInitialSize(canvasElement, sizeOptions);
|
|
1387
|
-
if (!is.equ(size, state.size, shallowLoose))
|
|
1388
|
-
state.setSize(size.width, size.height, size.top, size.left);
|
|
1389
|
-
// Check frameloop
|
|
1390
|
-
if (state.frameloop !== frameloop)
|
|
1391
|
-
state.setFrameloop(frameloop);
|
|
1392
|
-
if (!this.get('ready'))
|
|
1393
|
-
this.set({ ready: true });
|
|
1394
|
-
this.#invalidate();
|
|
1395
|
-
}
|
|
1396
|
-
destroy(canvas) {
|
|
1397
|
-
this.set((state) => ({ internal: { ...state.internal, active: false } }));
|
|
1398
|
-
setTimeout(() => {
|
|
1399
|
-
const { gl, xr, events } = this.get();
|
|
1400
|
-
if (gl) {
|
|
1401
|
-
if (events.disconnect) {
|
|
1402
|
-
events.disconnect();
|
|
1315
|
+
/*
|
|
1316
|
+
MAYBE TODO, DELETE IF NOT:
|
|
1317
|
+
Check if the object is captured, captured events should not have intersects running in parallel
|
|
1318
|
+
But wouldn't it be better to just replace capturedMap with a single entry?
|
|
1319
|
+
Also, are we OK with straight up making picking up multiple objects impossible?
|
|
1320
|
+
|
|
1321
|
+
const pointerId = (data as ThreeEvent<PointerEvent>).pointerId
|
|
1322
|
+
if (pointerId !== undefined) {
|
|
1323
|
+
const capturedMeshSet = internal.capturedMap.get(pointerId)
|
|
1324
|
+
if (capturedMeshSet) {
|
|
1325
|
+
const captured = capturedMeshSet.get(eventObject)
|
|
1326
|
+
if (captured && captured.localState.stopped) return
|
|
1327
|
+
}
|
|
1328
|
+
}*/
|
|
1329
|
+
if (isPointerMove) {
|
|
1330
|
+
// Move event ...
|
|
1331
|
+
if (handlers?.pointerover ||
|
|
1332
|
+
handlers?.pointerenter ||
|
|
1333
|
+
handlers?.pointerout ||
|
|
1334
|
+
handlers?.pointerleave) {
|
|
1335
|
+
// When enter or out is present take care of hover-state
|
|
1336
|
+
const id = makeId(data);
|
|
1337
|
+
const hoveredItem = internal.hovered.get(id);
|
|
1338
|
+
if (!hoveredItem) {
|
|
1339
|
+
// If the object wasn't previously hovered, book it and call its handler
|
|
1340
|
+
internal.hovered.set(id, data);
|
|
1341
|
+
handlers.pointerover?.(data);
|
|
1342
|
+
handlers.pointerenter?.(data);
|
|
1343
|
+
}
|
|
1344
|
+
else if (hoveredItem.stopped) {
|
|
1345
|
+
// If the object was previously hovered and stopped, we shouldn't allow other items to proceed
|
|
1346
|
+
data.stopPropagation();
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
// Call mouse move
|
|
1350
|
+
handlers?.pointermove?.(data);
|
|
1403
1351
|
}
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1352
|
+
else {
|
|
1353
|
+
// All other events ...
|
|
1354
|
+
const handler = handlers?.[name];
|
|
1355
|
+
if (handler) {
|
|
1356
|
+
// Forward all events back to their respective handlers with the exception of click events,
|
|
1357
|
+
// which must use the initial target
|
|
1358
|
+
if (!isClickEvent || internal.initialHits.includes(eventObject)) {
|
|
1359
|
+
// Missed events have to come first
|
|
1360
|
+
pointerMissed(event, internal.interaction.filter((object) => !internal.initialHits.includes(object)));
|
|
1361
|
+
// Now call the handler
|
|
1362
|
+
handler(data);
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
else {
|
|
1366
|
+
// Trigger onPointerMissed on all elements that have pointer over/out handlers, but not click and weren't hit
|
|
1367
|
+
if (isClickEvent && internal.initialHits.includes(eventObject)) {
|
|
1368
|
+
pointerMissed(event, internal.interaction.filter((object) => !internal.initialHits.includes(object)));
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1409
1371
|
}
|
|
1410
|
-
dispose(this.get());
|
|
1411
|
-
rootStateMap.delete(canvas);
|
|
1412
1372
|
}
|
|
1413
|
-
|
|
1373
|
+
handleIntersects(hits, event, delta, onIntersect);
|
|
1374
|
+
};
|
|
1414
1375
|
}
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1376
|
+
return { handlePointer };
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
const DOM_EVENTS = {
|
|
1380
|
+
click: false,
|
|
1381
|
+
contextmenu: false,
|
|
1382
|
+
dblclick: false,
|
|
1383
|
+
wheel: false,
|
|
1384
|
+
pointerdown: true,
|
|
1385
|
+
pointerup: true,
|
|
1386
|
+
pointerleave: true,
|
|
1387
|
+
pointermove: true,
|
|
1388
|
+
pointercancel: true,
|
|
1389
|
+
lostpointercapture: true,
|
|
1390
|
+
};
|
|
1391
|
+
const supportedEvents = [
|
|
1392
|
+
'click',
|
|
1393
|
+
'contextmenu',
|
|
1394
|
+
'dblclick',
|
|
1395
|
+
'pointerup',
|
|
1396
|
+
'pointerdown',
|
|
1397
|
+
'pointerover',
|
|
1398
|
+
'pointerout',
|
|
1399
|
+
'pointerenter',
|
|
1400
|
+
'pointerleave',
|
|
1401
|
+
'pointermove',
|
|
1402
|
+
'pointermissed',
|
|
1403
|
+
'pointercancel',
|
|
1404
|
+
'wheel',
|
|
1405
|
+
];
|
|
1406
|
+
function createPointerEvents(store) {
|
|
1407
|
+
const { handlePointer } = createEvents(store);
|
|
1408
|
+
return {
|
|
1409
|
+
priority: 1,
|
|
1410
|
+
enabled: true,
|
|
1411
|
+
compute: (event, root) => {
|
|
1412
|
+
const state = root.get();
|
|
1413
|
+
// https://github.com/pmndrs/react-three-fiber/pull/782
|
|
1414
|
+
// Events trigger outside of canvas when moved, use offsetX/Y by default and allow overrides
|
|
1415
|
+
state.pointer.set((event.offsetX / state.size.width) * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1);
|
|
1416
|
+
state.raycaster.setFromCamera(state.pointer, state.camera);
|
|
1417
|
+
},
|
|
1418
|
+
connected: undefined,
|
|
1419
|
+
handlers: Object.keys(DOM_EVENTS).reduce((handlers, supportedEventName) => {
|
|
1420
|
+
handlers[supportedEventName] = handlePointer(supportedEventName);
|
|
1421
|
+
return handlers;
|
|
1422
|
+
}, {}),
|
|
1423
|
+
update: () => {
|
|
1424
|
+
const { events, internal } = store.get();
|
|
1425
|
+
if (internal.lastEvent?.nativeElement && events.handlers)
|
|
1426
|
+
events.handlers.pointermove(internal.lastEvent.nativeElement);
|
|
1427
|
+
},
|
|
1428
|
+
connect: (target) => {
|
|
1429
|
+
const state = store.get();
|
|
1430
|
+
state.events.disconnect?.();
|
|
1431
|
+
state.setEvents({ connected: target });
|
|
1432
|
+
Object.entries(state.events.handlers ?? {}).forEach(([eventName, eventHandler]) => {
|
|
1433
|
+
const passive = DOM_EVENTS[eventName];
|
|
1434
|
+
target.addEventListener(eventName, eventHandler, { passive });
|
|
1435
|
+
});
|
|
1436
|
+
},
|
|
1437
|
+
disconnect: () => {
|
|
1438
|
+
const { events, setEvents } = store.get();
|
|
1439
|
+
if (events.connected) {
|
|
1440
|
+
Object.entries(events.handlers ?? {}).forEach(([eventName, eventHandler]) => {
|
|
1441
|
+
if (events.connected instanceof HTMLElement) {
|
|
1442
|
+
events.connected.removeEventListener(eventName, eventHandler);
|
|
1443
|
+
}
|
|
1444
|
+
});
|
|
1445
|
+
setEvents({ connected: undefined });
|
|
1434
1446
|
}
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1447
|
+
},
|
|
1448
|
+
};
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
const cached = new Map();
|
|
1452
|
+
function normalizeInputs(input) {
|
|
1453
|
+
if (Array.isArray(input))
|
|
1454
|
+
return input;
|
|
1455
|
+
if (typeof input === 'string')
|
|
1456
|
+
return [input];
|
|
1457
|
+
return Object.values(input);
|
|
1458
|
+
}
|
|
1459
|
+
function load(loaderConstructorFactory, inputs, { extensions, onProgress, } = {}) {
|
|
1460
|
+
return () => {
|
|
1461
|
+
const urls = normalizeInputs(inputs());
|
|
1462
|
+
const loader = new (loaderConstructorFactory(urls))();
|
|
1463
|
+
if (extensions)
|
|
1464
|
+
extensions(loader);
|
|
1465
|
+
// TODO: reevaluate this
|
|
1466
|
+
return urls.map((url) => {
|
|
1467
|
+
if (!cached.has(url)) {
|
|
1468
|
+
cached.set(url, new Promise((resolve, reject) => {
|
|
1469
|
+
loader.load(url, (data) => {
|
|
1470
|
+
if ('scene' in data)
|
|
1471
|
+
Object.assign(data, makeObjectGraph(data['scene']));
|
|
1472
|
+
resolve(data);
|
|
1473
|
+
}, onProgress, (error) => reject(new Error(`[NGT] Could not load ${url}: ${error}`)));
|
|
1441
1474
|
}));
|
|
1442
1475
|
}
|
|
1443
|
-
|
|
1476
|
+
return cached.get(url);
|
|
1477
|
+
});
|
|
1478
|
+
};
|
|
1479
|
+
}
|
|
1480
|
+
function _injectNgtLoader(loaderConstructorFactory, inputs, { extensions, onProgress, injector, } = {}) {
|
|
1481
|
+
injector = assertInjector(_injectNgtLoader, injector);
|
|
1482
|
+
const response = signal(null);
|
|
1483
|
+
return runInInjectionContext(injector, () => {
|
|
1484
|
+
const cdr = inject(ChangeDetectorRef);
|
|
1485
|
+
const effector = load(loaderConstructorFactory, inputs, { extensions, onProgress });
|
|
1486
|
+
effect(() => {
|
|
1487
|
+
const originalUrls = inputs();
|
|
1488
|
+
Promise.all(effector()).then((results) => {
|
|
1489
|
+
response.update(() => {
|
|
1490
|
+
if (Array.isArray(originalUrls))
|
|
1491
|
+
return results;
|
|
1492
|
+
if (typeof originalUrls === 'string')
|
|
1493
|
+
return results[0];
|
|
1494
|
+
const keys = Object.keys(originalUrls);
|
|
1495
|
+
return keys.reduce((result, key) => {
|
|
1496
|
+
result[key] = results[keys.indexOf(key)];
|
|
1497
|
+
return result;
|
|
1498
|
+
}, {});
|
|
1499
|
+
});
|
|
1500
|
+
safeDetectChanges(cdr);
|
|
1501
|
+
});
|
|
1502
|
+
});
|
|
1503
|
+
return response.asReadonly();
|
|
1504
|
+
});
|
|
1505
|
+
}
|
|
1506
|
+
_injectNgtLoader.preload = (loaderConstructorFactory, inputs, extensions) => {
|
|
1507
|
+
Promise.all(load(loaderConstructorFactory, inputs, { extensions })());
|
|
1508
|
+
};
|
|
1509
|
+
_injectNgtLoader.destroy = () => {
|
|
1510
|
+
cached.clear();
|
|
1511
|
+
};
|
|
1512
|
+
const injectNgtLoader = _injectNgtLoader;
|
|
1513
|
+
|
|
1514
|
+
const catalogue = {};
|
|
1515
|
+
function extend(objects) {
|
|
1516
|
+
Object.assign(catalogue, objects);
|
|
1517
|
+
}
|
|
1518
|
+
const [injectNgtCatalogue] = createInjectionToken(() => catalogue);
|
|
1519
|
+
|
|
1520
|
+
const ROUTED_SCENE = '__ngt_renderer_is_routed_scene__';
|
|
1521
|
+
const HTML = '__ngt_renderer_is_html';
|
|
1522
|
+
const SPECIAL_INTERNAL_ADD_COMMENT = '__ngt_renderer_add_comment__';
|
|
1523
|
+
const SPECIAL_DOM_TAG = {
|
|
1524
|
+
NGT_PORTAL: 'ngt-portal',
|
|
1525
|
+
NGT_PRIMITIVE: 'ngt-primitive',
|
|
1526
|
+
NGT_VALUE: 'ngt-value',
|
|
1527
|
+
};
|
|
1528
|
+
const SPECIAL_PROPERTIES = {
|
|
1529
|
+
COMPOUND: 'ngtCompound',
|
|
1530
|
+
RENDER_PRIORITY: 'priority',
|
|
1531
|
+
ATTACH: 'attach',
|
|
1532
|
+
VALUE: 'rawValue',
|
|
1533
|
+
REF: 'ref',
|
|
1534
|
+
};
|
|
1535
|
+
const SPECIAL_EVENTS = {
|
|
1536
|
+
BEFORE_RENDER: 'beforeRender',
|
|
1537
|
+
AFTER_UPDATE: 'afterUpdate',
|
|
1538
|
+
AFTER_ATTACH: 'afterAttach',
|
|
1539
|
+
};
|
|
1540
|
+
|
|
1541
|
+
class NgtCommonDirective {
|
|
1542
|
+
static { this.processComment = true; }
|
|
1543
|
+
constructor() {
|
|
1544
|
+
this.vcr = inject(ViewContainerRef);
|
|
1545
|
+
this.zone = inject(NgZone);
|
|
1546
|
+
this.template = inject(TemplateRef);
|
|
1547
|
+
this.injected = false;
|
|
1548
|
+
this.shouldCreateView = true;
|
|
1549
|
+
if (NgtCommonDirective.processComment) {
|
|
1550
|
+
const commentNode = this.vcr.element.nativeElement;
|
|
1551
|
+
if (commentNode[SPECIAL_INTERNAL_ADD_COMMENT]) {
|
|
1552
|
+
commentNode[SPECIAL_INTERNAL_ADD_COMMENT]();
|
|
1553
|
+
delete commentNode[SPECIAL_INTERNAL_ADD_COMMENT];
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
inject(DestroyRef).onDestroy(() => {
|
|
1557
|
+
this.view?.destroy();
|
|
1558
|
+
});
|
|
1444
1559
|
}
|
|
1445
|
-
|
|
1446
|
-
|
|
1560
|
+
createView() {
|
|
1561
|
+
this.zone.runOutsideAngular(() => {
|
|
1562
|
+
if (this.shouldCreateView) {
|
|
1563
|
+
if (this.view && !this.view.destroyed) {
|
|
1564
|
+
this.view.destroy();
|
|
1565
|
+
}
|
|
1566
|
+
this.view = this.vcr.createEmbeddedView(this.template);
|
|
1567
|
+
safeDetectChanges(this.view);
|
|
1568
|
+
}
|
|
1569
|
+
});
|
|
1447
1570
|
}
|
|
1448
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.
|
|
1449
|
-
static { this.ɵ
|
|
1571
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: NgtCommonDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1572
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.3", type: NgtCommonDirective, ngImport: i0 }); }
|
|
1450
1573
|
}
|
|
1451
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.
|
|
1452
|
-
type:
|
|
1453
|
-
}] });
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1574
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: NgtCommonDirective, decorators: [{
|
|
1575
|
+
type: Directive
|
|
1576
|
+
}], ctorParameters: function () { return []; } });
|
|
1577
|
+
|
|
1578
|
+
class NgtArgs extends NgtCommonDirective {
|
|
1579
|
+
constructor() {
|
|
1580
|
+
super(...arguments);
|
|
1581
|
+
this.injectedArgs = [];
|
|
1459
1582
|
}
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1583
|
+
set args(args) {
|
|
1584
|
+
if (args == null || !Array.isArray(args) || (args.length === 1 && args[0] === null))
|
|
1585
|
+
return;
|
|
1586
|
+
this.injected = false;
|
|
1587
|
+
this.injectedArgs = args;
|
|
1588
|
+
this.createView();
|
|
1589
|
+
}
|
|
1590
|
+
get args() {
|
|
1591
|
+
if (this.validate()) {
|
|
1592
|
+
this.injected = true;
|
|
1593
|
+
return this.injectedArgs;
|
|
1594
|
+
}
|
|
1595
|
+
return null;
|
|
1596
|
+
}
|
|
1597
|
+
validate() {
|
|
1598
|
+
return !this.injected && !!this.injectedArgs.length;
|
|
1469
1599
|
}
|
|
1600
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: NgtArgs, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1601
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.3", type: NgtArgs, isStandalone: true, selector: "ng-template[args]", inputs: { args: "args" }, usesInheritance: true, ngImport: i0 }); }
|
|
1470
1602
|
}
|
|
1603
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: NgtArgs, decorators: [{
|
|
1604
|
+
type: Directive,
|
|
1605
|
+
args: [{ selector: 'ng-template[args]', standalone: true }]
|
|
1606
|
+
}], propDecorators: { args: [{
|
|
1607
|
+
type: Input
|
|
1608
|
+
}] } });
|
|
1471
1609
|
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1610
|
+
class NgtParent extends NgtCommonDirective {
|
|
1611
|
+
constructor() {
|
|
1612
|
+
super(...arguments);
|
|
1613
|
+
this.injectedParent = null;
|
|
1614
|
+
}
|
|
1615
|
+
set parent(parent) {
|
|
1616
|
+
if (!parent)
|
|
1617
|
+
return;
|
|
1618
|
+
this.injected = false;
|
|
1619
|
+
this.injectedParent = parent;
|
|
1620
|
+
this.createView();
|
|
1621
|
+
}
|
|
1622
|
+
get parent() {
|
|
1623
|
+
if (this.validate()) {
|
|
1624
|
+
this.injected = true;
|
|
1625
|
+
return this.injectedParent;
|
|
1626
|
+
}
|
|
1627
|
+
return null;
|
|
1628
|
+
}
|
|
1629
|
+
validate() {
|
|
1630
|
+
return !this.injected && !!this.injectedParent;
|
|
1631
|
+
}
|
|
1632
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: NgtParent, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1633
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.3", type: NgtParent, isStandalone: true, selector: "ng-template[parent]", inputs: { parent: "parent" }, usesInheritance: true, ngImport: i0 }); }
|
|
1477
1634
|
}
|
|
1478
|
-
|
|
1635
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: NgtParent, decorators: [{
|
|
1636
|
+
type: Directive,
|
|
1637
|
+
args: [{ selector: 'ng-template[parent]', standalone: true }]
|
|
1638
|
+
}], propDecorators: { parent: [{
|
|
1639
|
+
type: Input
|
|
1640
|
+
}] } });
|
|
1479
1641
|
|
|
1480
1642
|
function attach(object, value, paths = []) {
|
|
1481
1643
|
const [base, ...remaining] = paths;
|
|
@@ -1507,25 +1669,38 @@ function createAttachFunction(cb) {
|
|
|
1507
1669
|
return (parent, child, store) => cb({ parent, child, store });
|
|
1508
1670
|
}
|
|
1509
1671
|
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
};
|
|
1672
|
+
// @internal
|
|
1673
|
+
var NgtRendererClassId;
|
|
1674
|
+
(function (NgtRendererClassId) {
|
|
1675
|
+
NgtRendererClassId[NgtRendererClassId["type"] = 0] = "type";
|
|
1676
|
+
NgtRendererClassId[NgtRendererClassId["parent"] = 1] = "parent";
|
|
1677
|
+
NgtRendererClassId[NgtRendererClassId["injectedParent"] = 2] = "injectedParent";
|
|
1678
|
+
NgtRendererClassId[NgtRendererClassId["children"] = 3] = "children";
|
|
1679
|
+
NgtRendererClassId[NgtRendererClassId["destroyed"] = 4] = "destroyed";
|
|
1680
|
+
NgtRendererClassId[NgtRendererClassId["compound"] = 5] = "compound";
|
|
1681
|
+
NgtRendererClassId[NgtRendererClassId["compoundParent"] = 6] = "compoundParent";
|
|
1682
|
+
NgtRendererClassId[NgtRendererClassId["compounded"] = 7] = "compounded";
|
|
1683
|
+
NgtRendererClassId[NgtRendererClassId["queueOps"] = 8] = "queueOps";
|
|
1684
|
+
NgtRendererClassId[NgtRendererClassId["attributes"] = 9] = "attributes";
|
|
1685
|
+
NgtRendererClassId[NgtRendererClassId["properties"] = 10] = "properties";
|
|
1686
|
+
NgtRendererClassId[NgtRendererClassId["rawValue"] = 11] = "rawValue";
|
|
1687
|
+
NgtRendererClassId[NgtRendererClassId["ref"] = 12] = "ref";
|
|
1688
|
+
NgtRendererClassId[NgtRendererClassId["portalContainer"] = 13] = "portalContainer";
|
|
1689
|
+
NgtRendererClassId[NgtRendererClassId["injectorFactory"] = 14] = "injectorFactory";
|
|
1690
|
+
})(NgtRendererClassId || (NgtRendererClassId = {}));
|
|
1691
|
+
// @internal
|
|
1692
|
+
var NgtCompoundClassId;
|
|
1693
|
+
(function (NgtCompoundClassId) {
|
|
1694
|
+
NgtCompoundClassId[NgtCompoundClassId["applyFirst"] = 0] = "applyFirst";
|
|
1695
|
+
NgtCompoundClassId[NgtCompoundClassId["props"] = 1] = "props";
|
|
1696
|
+
})(NgtCompoundClassId || (NgtCompoundClassId = {}));
|
|
1697
|
+
// @internal
|
|
1698
|
+
var NgtQueueOpClassId;
|
|
1699
|
+
(function (NgtQueueOpClassId) {
|
|
1700
|
+
NgtQueueOpClassId[NgtQueueOpClassId["type"] = 0] = "type";
|
|
1701
|
+
NgtQueueOpClassId[NgtQueueOpClassId["op"] = 1] = "op";
|
|
1702
|
+
NgtQueueOpClassId[NgtQueueOpClassId["done"] = 2] = "done";
|
|
1703
|
+
})(NgtQueueOpClassId || (NgtQueueOpClassId = {}));
|
|
1529
1704
|
function attachThreeChild(parent, child) {
|
|
1530
1705
|
const pLS = getLocalState(parent);
|
|
1531
1706
|
const cLS = getLocalState(child);
|
|
@@ -1536,7 +1711,7 @@ function attachThreeChild(parent, child) {
|
|
|
1536
1711
|
let added = false;
|
|
1537
1712
|
// assign store on child if not already exist
|
|
1538
1713
|
// or child store is the parent of parent store
|
|
1539
|
-
if (!cLS.store || cLS.store === pLS.store.get('
|
|
1714
|
+
if (!cLS.store || cLS.store === pLS.store.get('previousRoot')) {
|
|
1540
1715
|
cLS.store = pLS.store;
|
|
1541
1716
|
}
|
|
1542
1717
|
if (cLS.attach) {
|
|
@@ -1562,13 +1737,15 @@ function attachThreeChild(parent, child) {
|
|
|
1562
1737
|
}
|
|
1563
1738
|
// attach
|
|
1564
1739
|
if (cLS.isRaw) {
|
|
1565
|
-
if (cLS.parent) {
|
|
1566
|
-
|
|
1740
|
+
if (cLS.parent && cLS.parent() !== parent) {
|
|
1741
|
+
untracked(() => {
|
|
1742
|
+
cLS.parent.set(parent);
|
|
1743
|
+
});
|
|
1567
1744
|
}
|
|
1568
1745
|
// at this point we don't have rawValue yet, so we bail and wait until the Renderer recalls attach
|
|
1569
|
-
if (child.__ngt_renderer__[
|
|
1746
|
+
if (child.__ngt_renderer__[NgtRendererClassId.rawValue] === undefined)
|
|
1570
1747
|
return;
|
|
1571
|
-
attach(parent, child.__ngt_renderer__[
|
|
1748
|
+
attach(parent, child.__ngt_renderer__[NgtRendererClassId.rawValue], attachProp);
|
|
1572
1749
|
}
|
|
1573
1750
|
else {
|
|
1574
1751
|
attach(parent, child, attachProp);
|
|
@@ -1582,8 +1759,10 @@ function attachThreeChild(parent, child) {
|
|
|
1582
1759
|
added = true;
|
|
1583
1760
|
}
|
|
1584
1761
|
pLS.add(child, added ? 'objects' : 'nonObjects');
|
|
1585
|
-
if (cLS.parent) {
|
|
1586
|
-
|
|
1762
|
+
if (cLS.parent && cLS.parent() !== parent) {
|
|
1763
|
+
untracked(() => {
|
|
1764
|
+
cLS.parent.set(parent);
|
|
1765
|
+
});
|
|
1587
1766
|
}
|
|
1588
1767
|
if (cLS.afterAttach)
|
|
1589
1768
|
cLS.afterAttach.emit({ parent, node: child });
|
|
@@ -1594,11 +1773,13 @@ function removeThreeChild(parent, child, dispose) {
|
|
|
1594
1773
|
const pLS = getLocalState(parent);
|
|
1595
1774
|
const cLS = getLocalState(child);
|
|
1596
1775
|
// clear parent ref
|
|
1597
|
-
|
|
1776
|
+
untracked(() => {
|
|
1777
|
+
cLS.parent?.set(null);
|
|
1778
|
+
});
|
|
1598
1779
|
// remove child from parent
|
|
1599
|
-
if (untracked(pLS.objects))
|
|
1780
|
+
if (pLS.objects && untracked(pLS.objects))
|
|
1600
1781
|
pLS.remove(child, 'objects');
|
|
1601
|
-
if (untracked(pLS.nonObjects))
|
|
1782
|
+
if (pLS.nonObjects && untracked(pLS.nonObjects))
|
|
1602
1783
|
pLS.remove(child, 'nonObjects');
|
|
1603
1784
|
if (cLS.attach) {
|
|
1604
1785
|
detach(parent, child, cLS.attach);
|
|
@@ -1622,7 +1803,15 @@ function removeThreeRecursive(array, parent, dispose) {
|
|
|
1622
1803
|
if (array)
|
|
1623
1804
|
[...array].forEach((child) => removeThreeChild(parent, child, dispose));
|
|
1624
1805
|
}
|
|
1625
|
-
function
|
|
1806
|
+
function kebabToPascal(str) {
|
|
1807
|
+
// split the string at each hyphen
|
|
1808
|
+
const parts = str.split('-');
|
|
1809
|
+
// map over the parts, capitalizing the first letter of each part
|
|
1810
|
+
const pascalParts = parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1));
|
|
1811
|
+
// join the parts together to create the final PascalCase string
|
|
1812
|
+
return pascalParts.join('');
|
|
1813
|
+
}
|
|
1814
|
+
function processThreeEvent(instance, priority, eventName, callback, zone, rootCdr, targetCdr) {
|
|
1626
1815
|
const lS = getLocalState(instance);
|
|
1627
1816
|
if (eventName === SPECIAL_EVENTS.BEFORE_RENDER) {
|
|
1628
1817
|
return lS.store
|
|
@@ -1646,8 +1835,7 @@ function processThreeEvent(instance, priority, eventName, callback, zone, cdr, t
|
|
|
1646
1835
|
previousHandler(event);
|
|
1647
1836
|
zone.run(() => {
|
|
1648
1837
|
callback(event);
|
|
1649
|
-
safeDetectChanges(targetCdr);
|
|
1650
|
-
safeDetectChanges(cdr);
|
|
1838
|
+
safeDetectChanges(targetCdr, rootCdr);
|
|
1651
1839
|
});
|
|
1652
1840
|
};
|
|
1653
1841
|
Object.assign(lS.handlers, { [eventName]: updatedCallback });
|
|
@@ -1661,6 +1849,7 @@ function processThreeEvent(instance, priority, eventName, callback, zone, cdr, t
|
|
|
1661
1849
|
return () => {
|
|
1662
1850
|
const localState = getLocalState(instance);
|
|
1663
1851
|
if (localState && localState.eventCount) {
|
|
1852
|
+
localState.eventCount -= 1;
|
|
1664
1853
|
const index = localState.store
|
|
1665
1854
|
.get('internal', 'interaction')
|
|
1666
1855
|
.findIndex((obj) => obj.uuid === instance.uuid);
|
|
@@ -1669,117 +1858,12 @@ function processThreeEvent(instance, priority, eventName, callback, zone, cdr, t
|
|
|
1669
1858
|
}
|
|
1670
1859
|
};
|
|
1671
1860
|
}
|
|
1672
|
-
function eventToHandler(callback) {
|
|
1673
|
-
return (event) => {
|
|
1674
|
-
callback(event);
|
|
1675
|
-
};
|
|
1676
|
-
}
|
|
1677
|
-
function kebabToPascal(str) {
|
|
1678
|
-
// split the string at each hyphen
|
|
1679
|
-
const parts = str.split('-');
|
|
1680
|
-
// map over the parts, capitalizing the first letter of each part
|
|
1681
|
-
const pascalParts = parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1));
|
|
1682
|
-
// join the parts together to create the final PascalCase string
|
|
1683
|
-
return pascalParts.join('');
|
|
1684
|
-
}
|
|
1685
|
-
|
|
1686
|
-
class NgtCommonDirective {
|
|
1687
|
-
#vcr;
|
|
1688
|
-
#zone;
|
|
1689
|
-
#template;
|
|
1690
|
-
#view;
|
|
1691
|
-
constructor() {
|
|
1692
|
-
this.#vcr = inject(ViewContainerRef);
|
|
1693
|
-
this.#zone = inject(NgZone);
|
|
1694
|
-
this.#template = inject(TemplateRef);
|
|
1695
|
-
this.injected = false;
|
|
1696
|
-
this.shouldCreateView = true;
|
|
1697
|
-
const commentNode = this.#vcr.element.nativeElement;
|
|
1698
|
-
if (commentNode[SPECIAL_INTERNAL_ADD_COMMENT]) {
|
|
1699
|
-
commentNode[SPECIAL_INTERNAL_ADD_COMMENT]();
|
|
1700
|
-
delete commentNode[SPECIAL_INTERNAL_ADD_COMMENT];
|
|
1701
|
-
}
|
|
1702
|
-
}
|
|
1703
|
-
createView() {
|
|
1704
|
-
if (this.shouldCreateView) {
|
|
1705
|
-
if (this.#view && !this.#view.destroyed) {
|
|
1706
|
-
this.#view.destroy();
|
|
1707
|
-
}
|
|
1708
|
-
this.#zone.runOutsideAngular(() => {
|
|
1709
|
-
this.#view = this.#vcr.createEmbeddedView(this.#template);
|
|
1710
|
-
safeDetectChanges(this.#view);
|
|
1711
|
-
});
|
|
1712
|
-
}
|
|
1713
|
-
}
|
|
1714
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtCommonDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1715
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: NgtCommonDirective, ngImport: i0 }); }
|
|
1716
|
-
}
|
|
1717
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtCommonDirective, decorators: [{
|
|
1718
|
-
type: Directive
|
|
1719
|
-
}], ctorParameters: function () { return []; } });
|
|
1720
|
-
|
|
1721
|
-
class NgtArgs extends NgtCommonDirective {
|
|
1722
|
-
#injectedArgs = [];
|
|
1723
|
-
set args(args) {
|
|
1724
|
-
if (args == null || !Array.isArray(args) || (args.length === 1 && args[0] === null))
|
|
1725
|
-
return;
|
|
1726
|
-
this.injected = false;
|
|
1727
|
-
this.#injectedArgs = args;
|
|
1728
|
-
this.createView();
|
|
1729
|
-
}
|
|
1730
|
-
get args() {
|
|
1731
|
-
if (this.validate()) {
|
|
1732
|
-
this.injected = true;
|
|
1733
|
-
return this.#injectedArgs;
|
|
1734
|
-
}
|
|
1735
|
-
return null;
|
|
1736
|
-
}
|
|
1737
|
-
validate() {
|
|
1738
|
-
return !this.injected && !!this.#injectedArgs.length;
|
|
1739
|
-
}
|
|
1740
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtArgs, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1741
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: NgtArgs, isStandalone: true, selector: "[args]", inputs: { args: "args" }, usesInheritance: true, ngImport: i0 }); }
|
|
1742
|
-
}
|
|
1743
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtArgs, decorators: [{
|
|
1744
|
-
type: Directive,
|
|
1745
|
-
args: [{ selector: '[args]', standalone: true }]
|
|
1746
|
-
}], propDecorators: { args: [{
|
|
1747
|
-
type: Input
|
|
1748
|
-
}] } });
|
|
1749
|
-
|
|
1750
|
-
class NgtParent extends NgtCommonDirective {
|
|
1751
|
-
#injectedParent = null;
|
|
1752
|
-
set parent(parent) {
|
|
1753
|
-
if (!parent)
|
|
1754
|
-
return;
|
|
1755
|
-
this.injected = false;
|
|
1756
|
-
this.#injectedParent = parent;
|
|
1757
|
-
this.createView();
|
|
1758
|
-
}
|
|
1759
|
-
get parent() {
|
|
1760
|
-
if (this.validate()) {
|
|
1761
|
-
this.injected = true;
|
|
1762
|
-
return this.#injectedParent;
|
|
1763
|
-
}
|
|
1764
|
-
return null;
|
|
1765
|
-
}
|
|
1766
|
-
validate() {
|
|
1767
|
-
return !this.injected && !!this.#injectedParent;
|
|
1768
|
-
}
|
|
1769
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtParent, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1770
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: NgtParent, isStandalone: true, selector: "[parent]", inputs: { parent: "parent" }, usesInheritance: true, ngImport: i0 }); }
|
|
1771
|
-
}
|
|
1772
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtParent, decorators: [{
|
|
1773
|
-
type: Directive,
|
|
1774
|
-
args: [{ selector: '[parent]', standalone: true }]
|
|
1775
|
-
}], propDecorators: { parent: [{
|
|
1776
|
-
type: Input
|
|
1777
|
-
}] } });
|
|
1778
1861
|
|
|
1862
|
+
const NGT_COMPOUND_PREFIXES = new InjectionToken('NgtCompoundPrefixes');
|
|
1779
1863
|
class NgtRendererStore {
|
|
1780
|
-
#comments = [];
|
|
1781
1864
|
constructor(root) {
|
|
1782
1865
|
this.root = root;
|
|
1866
|
+
this.comments = [];
|
|
1783
1867
|
}
|
|
1784
1868
|
createNode(type, node) {
|
|
1785
1869
|
const state = [
|
|
@@ -1800,99 +1884,108 @@ class NgtRendererStore {
|
|
|
1800
1884
|
undefined,
|
|
1801
1885
|
];
|
|
1802
1886
|
const rendererNode = Object.assign(node, { __ngt_renderer__: state });
|
|
1803
|
-
// assign ownerDocument to node so we can use HostListener in Component
|
|
1887
|
+
// NOTE: assign ownerDocument to node so we can use HostListener in Component
|
|
1804
1888
|
if (!rendererNode['ownerDocument'])
|
|
1805
1889
|
rendererNode['ownerDocument'] = this.root.document;
|
|
1806
|
-
// assign injectorFactory on non-three type since
|
|
1890
|
+
// NOTE: assign injectorFactory on non-three type since
|
|
1807
1891
|
// rendererNode is an instance of DOM Node
|
|
1808
|
-
if (state[
|
|
1809
|
-
state[
|
|
1892
|
+
if (state[NgtRendererClassId.type] !== 'three') {
|
|
1893
|
+
state[NgtRendererClassId.injectorFactory] = () => getDebugNode(rendererNode).injector;
|
|
1810
1894
|
}
|
|
1811
|
-
if (state[
|
|
1895
|
+
if (state[NgtRendererClassId.type] === 'comment') {
|
|
1812
1896
|
// NOTE: we attach an arrow function to the Comment node
|
|
1813
1897
|
// In our directives, we can call this function to then start tracking the RendererNode
|
|
1814
1898
|
// this is done to limit the amount of Nodes we need to process for getCreationState
|
|
1815
1899
|
rendererNode[SPECIAL_INTERNAL_ADD_COMMENT] = (node) => {
|
|
1816
|
-
if (node && node.__ngt_renderer__[
|
|
1900
|
+
if (node && node.__ngt_renderer__[NgtRendererClassId.type] === 'portal') {
|
|
1817
1901
|
this.portals.push(node);
|
|
1818
1902
|
}
|
|
1819
1903
|
else {
|
|
1820
|
-
this
|
|
1904
|
+
this.comments.push(rendererNode);
|
|
1821
1905
|
}
|
|
1822
1906
|
};
|
|
1823
1907
|
return rendererNode;
|
|
1824
1908
|
}
|
|
1825
|
-
if (state[
|
|
1826
|
-
state[
|
|
1827
|
-
state[
|
|
1828
|
-
state[
|
|
1909
|
+
if (state[NgtRendererClassId.type] === 'compound') {
|
|
1910
|
+
state[NgtRendererClassId.queueOps] = new Set();
|
|
1911
|
+
state[NgtRendererClassId.attributes] = {};
|
|
1912
|
+
state[NgtRendererClassId.properties] = {};
|
|
1829
1913
|
return rendererNode;
|
|
1830
1914
|
}
|
|
1831
1915
|
return rendererNode;
|
|
1832
1916
|
}
|
|
1833
1917
|
setParent(node, parent) {
|
|
1834
|
-
if (!node.__ngt_renderer__[
|
|
1835
|
-
node.__ngt_renderer__[
|
|
1918
|
+
if (!node.__ngt_renderer__[NgtRendererClassId.parent]) {
|
|
1919
|
+
node.__ngt_renderer__[NgtRendererClassId.parent] = parent;
|
|
1836
1920
|
}
|
|
1837
1921
|
}
|
|
1838
1922
|
addChild(node, child) {
|
|
1839
|
-
if (!node.__ngt_renderer__[
|
|
1840
|
-
node.__ngt_renderer__[
|
|
1923
|
+
if (!node.__ngt_renderer__[NgtRendererClassId.children].includes(child)) {
|
|
1924
|
+
node.__ngt_renderer__[NgtRendererClassId.children].push(child);
|
|
1841
1925
|
}
|
|
1842
1926
|
}
|
|
1843
1927
|
removeChild(node, child) {
|
|
1844
|
-
const index = node.__ngt_renderer__[
|
|
1928
|
+
const index = node.__ngt_renderer__?.[NgtRendererClassId.children].findIndex((c) => child === c);
|
|
1845
1929
|
if (index >= 0) {
|
|
1846
|
-
node.__ngt_renderer__[
|
|
1930
|
+
node.__ngt_renderer__[NgtRendererClassId.children].splice(index, 1);
|
|
1847
1931
|
}
|
|
1848
1932
|
}
|
|
1849
1933
|
setCompound(compound, instance) {
|
|
1934
|
+
const instanceRS = instance.__ngt_renderer__;
|
|
1935
|
+
if (instanceRS && instanceRS[NgtRendererClassId.parent]) {
|
|
1936
|
+
const parentRS = instanceRS[NgtRendererClassId.parent].__ngt_renderer__;
|
|
1937
|
+
// NOTE: if instance is already compounded by its parent. skip
|
|
1938
|
+
if (parentRS[NgtRendererClassId.type] === 'compound' &&
|
|
1939
|
+
parentRS[NgtRendererClassId.compounded] === instance) {
|
|
1940
|
+
return;
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1850
1943
|
const rS = compound.__ngt_renderer__;
|
|
1851
|
-
rS[
|
|
1852
|
-
const attributes = Object.keys(rS[
|
|
1853
|
-
const properties = Object.keys(rS[
|
|
1944
|
+
rS[NgtRendererClassId.compounded] = instance;
|
|
1945
|
+
const attributes = Object.keys(rS[NgtRendererClassId.attributes]);
|
|
1946
|
+
const properties = Object.keys(rS[NgtRendererClassId.properties]);
|
|
1854
1947
|
for (const key of attributes) {
|
|
1855
|
-
this.applyAttribute(instance, key, rS[
|
|
1948
|
+
this.applyAttribute(instance, key, rS[NgtRendererClassId.attributes][key]);
|
|
1856
1949
|
}
|
|
1857
1950
|
for (const key of properties) {
|
|
1858
|
-
this.applyProperty(instance, key, rS[
|
|
1951
|
+
this.applyProperty(instance, key, rS[NgtRendererClassId.properties][key]);
|
|
1859
1952
|
}
|
|
1860
1953
|
this.executeOperation(compound);
|
|
1861
1954
|
}
|
|
1862
1955
|
queueOperation(node, op) {
|
|
1863
|
-
node.__ngt_renderer__[
|
|
1956
|
+
node.__ngt_renderer__[NgtRendererClassId.queueOps].add(op);
|
|
1864
1957
|
}
|
|
1865
1958
|
executeOperation(node, type = 'op') {
|
|
1866
1959
|
const rS = node.__ngt_renderer__;
|
|
1867
|
-
if (rS[
|
|
1868
|
-
rS[
|
|
1869
|
-
if (op[
|
|
1870
|
-
op[
|
|
1871
|
-
rS[
|
|
1960
|
+
if (rS[NgtRendererClassId.queueOps]?.size) {
|
|
1961
|
+
rS[NgtRendererClassId.queueOps].forEach((op) => {
|
|
1962
|
+
if (op[NgtQueueOpClassId.type] === type) {
|
|
1963
|
+
op[NgtQueueOpClassId.op]();
|
|
1964
|
+
rS[NgtRendererClassId.queueOps].delete(op);
|
|
1872
1965
|
}
|
|
1873
1966
|
});
|
|
1874
1967
|
}
|
|
1875
1968
|
}
|
|
1876
1969
|
processPortalContainer(portal) {
|
|
1877
|
-
const injectorFactory = portal.__ngt_renderer__[
|
|
1970
|
+
const injectorFactory = portal.__ngt_renderer__[NgtRendererClassId.injectorFactory];
|
|
1878
1971
|
const injector = injectorFactory?.();
|
|
1879
1972
|
if (!injector)
|
|
1880
1973
|
return;
|
|
1881
|
-
const portalStore = injector.get(
|
|
1974
|
+
const portalStore = injector.get(NGT_STORE, null);
|
|
1882
1975
|
if (!portalStore)
|
|
1883
1976
|
return;
|
|
1884
1977
|
const portalContainer = portalStore.get('scene');
|
|
1885
1978
|
if (!portalContainer)
|
|
1886
1979
|
return;
|
|
1887
|
-
portal.__ngt_renderer__[
|
|
1980
|
+
portal.__ngt_renderer__[NgtRendererClassId.portalContainer] = this.createNode('three', portalContainer);
|
|
1888
1981
|
}
|
|
1889
1982
|
applyAttribute(node, name, value) {
|
|
1890
1983
|
const rS = node.__ngt_renderer__;
|
|
1891
|
-
if (rS[
|
|
1984
|
+
if (rS[NgtRendererClassId.destroyed])
|
|
1892
1985
|
return;
|
|
1893
1986
|
if (name === SPECIAL_PROPERTIES.RENDER_PRIORITY) {
|
|
1894
|
-
// priority needs to be set as an attribute string so that they can be set as early as possible
|
|
1895
|
-
// we convert that string to a number. if it's invalid, 0
|
|
1987
|
+
// NOTE: priority needs to be set as an attribute string so that they can be set as early as possible
|
|
1988
|
+
// we convert that string to a number. if it's invalid, default 0
|
|
1896
1989
|
let priority = Number(value);
|
|
1897
1990
|
if (isNaN(priority)) {
|
|
1898
1991
|
priority = 0;
|
|
@@ -1901,19 +1994,19 @@ class NgtRendererStore {
|
|
|
1901
1994
|
getLocalState(node).priority = priority;
|
|
1902
1995
|
}
|
|
1903
1996
|
if (name === SPECIAL_PROPERTIES.COMPOUND) {
|
|
1904
|
-
// we set the compound property on instance node now so we know that this instance is being compounded
|
|
1905
|
-
rS[
|
|
1997
|
+
// NOTE: we set the compound property on instance node now so we know that this instance is being compounded
|
|
1998
|
+
rS[NgtRendererClassId.compound] = [value === '' || value === 'first', {}];
|
|
1906
1999
|
return;
|
|
1907
2000
|
}
|
|
1908
2001
|
if (name === SPECIAL_PROPERTIES.ATTACH) {
|
|
1909
|
-
// handle attach as tring
|
|
2002
|
+
// NOTE: handle attach as tring
|
|
1910
2003
|
const paths = value.split('.');
|
|
1911
2004
|
if (paths.length)
|
|
1912
2005
|
getLocalState(node).attach = paths;
|
|
1913
2006
|
return;
|
|
1914
2007
|
}
|
|
1915
2008
|
if (name === SPECIAL_PROPERTIES.VALUE) {
|
|
1916
|
-
// coercion
|
|
2009
|
+
// NOTE: coercion
|
|
1917
2010
|
let maybeCoerced = value;
|
|
1918
2011
|
if (maybeCoerced === '' || maybeCoerced === 'true' || maybeCoerced === 'false') {
|
|
1919
2012
|
maybeCoerced = maybeCoerced === 'true' || maybeCoerced === '';
|
|
@@ -1921,25 +2014,26 @@ class NgtRendererStore {
|
|
|
1921
2014
|
else if (!isNaN(Number(maybeCoerced))) {
|
|
1922
2015
|
maybeCoerced = Number(maybeCoerced);
|
|
1923
2016
|
}
|
|
1924
|
-
rS[
|
|
2017
|
+
rS[NgtRendererClassId.rawValue] = maybeCoerced;
|
|
1925
2018
|
return;
|
|
1926
2019
|
}
|
|
1927
2020
|
applyProps(node, { [name]: value });
|
|
2021
|
+
this.updateNativeProps(node, name, value);
|
|
1928
2022
|
}
|
|
1929
2023
|
applyProperty(node, name, value) {
|
|
1930
2024
|
const rS = node.__ngt_renderer__;
|
|
1931
|
-
if (rS[
|
|
2025
|
+
if (rS[NgtRendererClassId.destroyed])
|
|
1932
2026
|
return;
|
|
1933
2027
|
// [ref]
|
|
1934
2028
|
if (name === SPECIAL_PROPERTIES.REF && is.ref(value)) {
|
|
1935
|
-
rS[
|
|
2029
|
+
rS[NgtRendererClassId.ref] = value;
|
|
1936
2030
|
value.nativeElement = node;
|
|
1937
2031
|
return;
|
|
1938
2032
|
}
|
|
1939
|
-
const parent = getLocalState(node).parent() || rS[
|
|
2033
|
+
const parent = getLocalState(node).parent() || rS[NgtRendererClassId.parent];
|
|
1940
2034
|
// [rawValue]
|
|
1941
2035
|
if (getLocalState(node).isRaw && name === SPECIAL_PROPERTIES.VALUE) {
|
|
1942
|
-
rS[
|
|
2036
|
+
rS[NgtRendererClassId.rawValue] = value;
|
|
1943
2037
|
if (parent)
|
|
1944
2038
|
attachThreeChild(parent, node);
|
|
1945
2039
|
return;
|
|
@@ -1951,13 +2045,14 @@ class NgtRendererStore {
|
|
|
1951
2045
|
attachThreeChild(parent, node);
|
|
1952
2046
|
return;
|
|
1953
2047
|
}
|
|
1954
|
-
const compound = rS[
|
|
1955
|
-
if (compound?.[
|
|
1956
|
-
name in compound[
|
|
1957
|
-
!compound[
|
|
1958
|
-
value = compound[
|
|
2048
|
+
const compound = rS[NgtRendererClassId.compound];
|
|
2049
|
+
if (compound?.[NgtCompoundClassId.props] &&
|
|
2050
|
+
name in compound[NgtCompoundClassId.props] &&
|
|
2051
|
+
!compound[NgtCompoundClassId.applyFirst]) {
|
|
2052
|
+
value = compound[NgtCompoundClassId.props][name];
|
|
1959
2053
|
}
|
|
1960
2054
|
applyProps(node, { [name]: value });
|
|
2055
|
+
this.updateNativeProps(node, name, value);
|
|
1961
2056
|
}
|
|
1962
2057
|
isCompound(name) {
|
|
1963
2058
|
return this.root.compoundPrefixes.some((prefix) => name.startsWith(prefix));
|
|
@@ -1965,7 +2060,7 @@ class NgtRendererStore {
|
|
|
1965
2060
|
isDOM(node) {
|
|
1966
2061
|
const rS = node['__ngt_renderer__'];
|
|
1967
2062
|
return (!rS ||
|
|
1968
|
-
(rS[
|
|
2063
|
+
(rS[NgtRendererClassId.type] !== 'compound' &&
|
|
1969
2064
|
(node instanceof Element || node instanceof Document || node instanceof Window)));
|
|
1970
2065
|
}
|
|
1971
2066
|
get rootScene() {
|
|
@@ -1975,54 +2070,54 @@ class NgtRendererStore {
|
|
|
1975
2070
|
return this.root.portals;
|
|
1976
2071
|
}
|
|
1977
2072
|
getClosestParentWithInstance(node) {
|
|
1978
|
-
let parent = node.__ngt_renderer__[
|
|
1979
|
-
while (parent && parent.__ngt_renderer__[
|
|
1980
|
-
parent = parent.__ngt_renderer__[
|
|
1981
|
-
? parent.__ngt_renderer__[
|
|
1982
|
-
: parent.__ngt_renderer__[
|
|
2073
|
+
let parent = node.__ngt_renderer__[NgtRendererClassId.parent];
|
|
2074
|
+
while (parent && parent.__ngt_renderer__[NgtRendererClassId.type] !== 'three') {
|
|
2075
|
+
parent = parent.__ngt_renderer__[NgtRendererClassId.portalContainer]
|
|
2076
|
+
? parent.__ngt_renderer__[NgtRendererClassId.portalContainer]
|
|
2077
|
+
: parent.__ngt_renderer__[NgtRendererClassId.parent];
|
|
1983
2078
|
}
|
|
1984
2079
|
return parent;
|
|
1985
2080
|
}
|
|
1986
2081
|
getClosestParentWithCompound(node) {
|
|
1987
|
-
if (node.__ngt_renderer__[
|
|
1988
|
-
return node.__ngt_renderer__[
|
|
2082
|
+
if (node.__ngt_renderer__[NgtRendererClassId.compoundParent]) {
|
|
2083
|
+
return node.__ngt_renderer__[NgtRendererClassId.compoundParent];
|
|
1989
2084
|
}
|
|
1990
|
-
let parent = node.__ngt_renderer__[
|
|
2085
|
+
let parent = node.__ngt_renderer__[NgtRendererClassId.parent];
|
|
1991
2086
|
if (parent &&
|
|
1992
|
-
parent.__ngt_renderer__[
|
|
1993
|
-
!parent.__ngt_renderer__[
|
|
2087
|
+
parent.__ngt_renderer__[NgtRendererClassId.type] === 'compound' &&
|
|
2088
|
+
!parent.__ngt_renderer__[NgtRendererClassId.compounded]) {
|
|
1994
2089
|
return parent;
|
|
1995
2090
|
}
|
|
1996
2091
|
while (parent &&
|
|
1997
|
-
(parent.__ngt_renderer__[
|
|
1998
|
-
!parent.__ngt_renderer__[
|
|
1999
|
-
parent.__ngt_renderer__[
|
|
2000
|
-
parent = parent.__ngt_renderer__[
|
|
2092
|
+
(parent.__ngt_renderer__[NgtRendererClassId.type] === 'three' ||
|
|
2093
|
+
!parent.__ngt_renderer__[NgtRendererClassId.compoundParent] ||
|
|
2094
|
+
parent.__ngt_renderer__[NgtRendererClassId.type] !== 'compound')) {
|
|
2095
|
+
parent = parent.__ngt_renderer__[NgtRendererClassId.parent];
|
|
2001
2096
|
}
|
|
2002
2097
|
if (!parent)
|
|
2003
2098
|
return;
|
|
2004
|
-
if (parent.__ngt_renderer__[
|
|
2005
|
-
parent.__ngt_renderer__[
|
|
2006
|
-
return parent.__ngt_renderer__[
|
|
2099
|
+
if (parent.__ngt_renderer__[NgtRendererClassId.type] === 'three' &&
|
|
2100
|
+
parent.__ngt_renderer__[NgtRendererClassId.compoundParent]) {
|
|
2101
|
+
return parent.__ngt_renderer__[NgtRendererClassId.compoundParent];
|
|
2007
2102
|
}
|
|
2008
|
-
if (!parent.__ngt_renderer__[
|
|
2103
|
+
if (!parent.__ngt_renderer__[NgtRendererClassId.compounded]) {
|
|
2009
2104
|
return parent;
|
|
2010
2105
|
}
|
|
2011
2106
|
return null;
|
|
2012
2107
|
}
|
|
2013
2108
|
getCreationState() {
|
|
2014
|
-
const injectedArgs = this
|
|
2015
|
-
const injectedParent = this
|
|
2016
|
-
const store = this
|
|
2109
|
+
const injectedArgs = this.firstNonInjectedDirective(NgtArgs)?.args || [];
|
|
2110
|
+
const injectedParent = this.firstNonInjectedDirective(NgtParent)?.parent || null;
|
|
2111
|
+
const store = this.tryGetPortalStore();
|
|
2017
2112
|
return { injectedArgs, injectedParent, store };
|
|
2018
2113
|
}
|
|
2019
2114
|
destroy(node, parent) {
|
|
2020
2115
|
const rS = node.__ngt_renderer__;
|
|
2021
|
-
if (rS
|
|
2116
|
+
if (!rS || rS[NgtRendererClassId.destroyed])
|
|
2022
2117
|
return;
|
|
2023
|
-
if (rS[
|
|
2024
|
-
rS[
|
|
2025
|
-
rS[
|
|
2118
|
+
if (rS[NgtRendererClassId.type] === 'three') {
|
|
2119
|
+
rS[NgtRendererClassId.compound] = undefined;
|
|
2120
|
+
rS[NgtRendererClassId.compoundParent] = undefined;
|
|
2026
2121
|
const localState = getLocalState(node);
|
|
2027
2122
|
if (localState.objects) {
|
|
2028
2123
|
untracked(localState.objects).forEach((obj) => this.destroy(obj, parent));
|
|
@@ -2047,58 +2142,73 @@ class NgtRendererStore {
|
|
|
2047
2142
|
delete node['__ngt__'];
|
|
2048
2143
|
}
|
|
2049
2144
|
}
|
|
2050
|
-
if (rS[
|
|
2051
|
-
rS[
|
|
2145
|
+
if (rS[NgtRendererClassId.type] === 'comment') {
|
|
2146
|
+
rS[NgtRendererClassId.injectorFactory] = null;
|
|
2052
2147
|
delete node[SPECIAL_INTERNAL_ADD_COMMENT];
|
|
2053
|
-
const index = this
|
|
2148
|
+
const index = this.comments.findIndex((comment) => comment === node);
|
|
2054
2149
|
if (index > -1) {
|
|
2055
|
-
this
|
|
2150
|
+
this.comments.splice(index, 1);
|
|
2056
2151
|
}
|
|
2057
2152
|
}
|
|
2058
|
-
if (rS[
|
|
2059
|
-
rS[
|
|
2153
|
+
if (rS[NgtRendererClassId.type] === 'portal') {
|
|
2154
|
+
rS[NgtRendererClassId.injectorFactory] = null;
|
|
2060
2155
|
const index = this.portals.findIndex((portal) => portal === node);
|
|
2061
2156
|
if (index > -1) {
|
|
2062
2157
|
this.portals.splice(index, 1);
|
|
2063
2158
|
}
|
|
2064
2159
|
}
|
|
2065
|
-
if (rS[
|
|
2066
|
-
rS[
|
|
2067
|
-
rS[
|
|
2068
|
-
rS[
|
|
2160
|
+
if (rS[NgtRendererClassId.type] === 'compound') {
|
|
2161
|
+
rS[NgtRendererClassId.compounded] = undefined;
|
|
2162
|
+
rS[NgtRendererClassId.attributes] = null;
|
|
2163
|
+
rS[NgtRendererClassId.properties] = null;
|
|
2069
2164
|
this.executeOperation(node, 'cleanUp');
|
|
2070
|
-
rS[
|
|
2071
|
-
rS[
|
|
2165
|
+
rS[NgtRendererClassId.queueOps].clear();
|
|
2166
|
+
rS[NgtRendererClassId.queueOps] = null;
|
|
2072
2167
|
}
|
|
2073
|
-
if (rS[
|
|
2168
|
+
if (rS[NgtRendererClassId.ref]) {
|
|
2074
2169
|
// nullify ref
|
|
2075
|
-
|
|
2076
|
-
|
|
2170
|
+
// but we do it later so that it doesn't hinder render
|
|
2171
|
+
// TODO: will this cause memory leak?
|
|
2172
|
+
requestAnimationFrame(() => {
|
|
2173
|
+
rS[NgtRendererClassId.ref].nativeElement = null;
|
|
2174
|
+
rS[NgtRendererClassId.ref] = undefined;
|
|
2175
|
+
});
|
|
2077
2176
|
}
|
|
2078
2177
|
// nullify parent
|
|
2079
|
-
rS[
|
|
2080
|
-
for (const renderChild of rS[
|
|
2081
|
-
if (renderChild.__ngt_renderer__[
|
|
2082
|
-
|
|
2178
|
+
rS[NgtRendererClassId.parent] = null;
|
|
2179
|
+
for (const renderChild of rS[NgtRendererClassId.children] || []) {
|
|
2180
|
+
if (renderChild.__ngt_renderer__?.[NgtRendererClassId.type] === 'three' && parent) {
|
|
2181
|
+
const closestInstance = this.getClosestParentWithInstance(parent);
|
|
2182
|
+
if (closestInstance) {
|
|
2183
|
+
removeThreeChild(closestInstance, renderChild, true);
|
|
2184
|
+
}
|
|
2083
2185
|
}
|
|
2084
2186
|
this.destroy(renderChild, parent);
|
|
2085
2187
|
}
|
|
2086
|
-
rS[
|
|
2087
|
-
rS[
|
|
2188
|
+
rS[NgtRendererClassId.children] = [];
|
|
2189
|
+
rS[NgtRendererClassId.destroyed] = true;
|
|
2088
2190
|
if (parent) {
|
|
2089
2191
|
this.removeChild(parent, node);
|
|
2090
2192
|
}
|
|
2091
2193
|
}
|
|
2092
|
-
|
|
2194
|
+
updateNativeProps(node, key, value) {
|
|
2195
|
+
const localState = getLocalState(node);
|
|
2196
|
+
if (!localState || !localState.nativeProps)
|
|
2197
|
+
return;
|
|
2198
|
+
localState.nativeProps.set({ [key]: value });
|
|
2199
|
+
}
|
|
2200
|
+
firstNonInjectedDirective(dir) {
|
|
2093
2201
|
let directive;
|
|
2094
|
-
|
|
2202
|
+
const destroyed = [];
|
|
2203
|
+
let i = this.comments.length - 1;
|
|
2095
2204
|
while (i >= 0) {
|
|
2096
|
-
const comment = this
|
|
2097
|
-
if (comment.__ngt_renderer__[
|
|
2205
|
+
const comment = this.comments[i];
|
|
2206
|
+
if (comment.__ngt_renderer__[NgtRendererClassId.destroyed]) {
|
|
2207
|
+
destroyed.push(i);
|
|
2098
2208
|
i--;
|
|
2099
2209
|
continue;
|
|
2100
2210
|
}
|
|
2101
|
-
const injector = comment.__ngt_renderer__[
|
|
2211
|
+
const injector = comment.__ngt_renderer__[NgtRendererClassId.injectorFactory]();
|
|
2102
2212
|
if (!injector) {
|
|
2103
2213
|
i--;
|
|
2104
2214
|
continue;
|
|
@@ -2110,73 +2220,85 @@ class NgtRendererStore {
|
|
|
2110
2220
|
}
|
|
2111
2221
|
i--;
|
|
2112
2222
|
}
|
|
2223
|
+
destroyed.forEach((index) => {
|
|
2224
|
+
this.comments.splice(index, 1);
|
|
2225
|
+
});
|
|
2113
2226
|
return directive;
|
|
2114
2227
|
}
|
|
2115
|
-
|
|
2228
|
+
tryGetPortalStore() {
|
|
2116
2229
|
let store;
|
|
2230
|
+
const destroyed = [];
|
|
2117
2231
|
// we only care about the portal states because NgtStore only differs per Portal
|
|
2118
2232
|
let i = this.portals.length - 1;
|
|
2119
2233
|
while (i >= 0) {
|
|
2120
2234
|
// loop through the portal state backwards to find the closest NgtStore
|
|
2121
2235
|
const portal = this.portals[i];
|
|
2122
|
-
if (portal.__ngt_renderer__[
|
|
2236
|
+
if (portal.__ngt_renderer__[NgtRendererClassId.destroyed]) {
|
|
2237
|
+
destroyed.push(i);
|
|
2123
2238
|
i--;
|
|
2124
2239
|
continue;
|
|
2125
2240
|
}
|
|
2126
|
-
const injector = portal.__ngt_renderer__[
|
|
2241
|
+
const injector = portal.__ngt_renderer__[NgtRendererClassId.injectorFactory]();
|
|
2127
2242
|
if (!injector) {
|
|
2128
2243
|
i--;
|
|
2129
2244
|
continue;
|
|
2130
2245
|
}
|
|
2131
|
-
const instance = injector.get(
|
|
2132
|
-
// only the instance with
|
|
2133
|
-
if (instance && instance.get('
|
|
2246
|
+
const instance = injector.get(NGT_STORE, null);
|
|
2247
|
+
// only the instance with previousRoot should pass
|
|
2248
|
+
if (instance && instance.get('previousRoot')) {
|
|
2134
2249
|
store = instance;
|
|
2135
2250
|
break;
|
|
2136
2251
|
}
|
|
2137
2252
|
i--;
|
|
2138
2253
|
}
|
|
2254
|
+
destroyed.forEach((index) => {
|
|
2255
|
+
this.portals.splice(index, 1);
|
|
2256
|
+
});
|
|
2139
2257
|
return store || this.root.store;
|
|
2140
2258
|
}
|
|
2141
2259
|
}
|
|
2142
2260
|
|
|
2143
2261
|
class NgtRendererFactory {
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2262
|
+
constructor() {
|
|
2263
|
+
this.delegateRendererFactory = inject(RendererFactory2, { skipSelf: true });
|
|
2264
|
+
this.zone = inject(NgZone);
|
|
2265
|
+
this.catalogue = injectNgtCatalogue();
|
|
2266
|
+
this.cdr = inject(ChangeDetectorRef);
|
|
2267
|
+
this.rendererMap = new Map();
|
|
2268
|
+
this.routedSet = new Set();
|
|
2269
|
+
// all Renderer instances share the same Store
|
|
2270
|
+
this.rendererStore = new NgtRendererStore({
|
|
2271
|
+
portals: [],
|
|
2272
|
+
store: injectNgtStore(),
|
|
2273
|
+
compoundPrefixes: inject(NGT_COMPOUND_PREFIXES),
|
|
2274
|
+
document: inject(DOCUMENT),
|
|
2275
|
+
});
|
|
2276
|
+
}
|
|
2157
2277
|
createRenderer(hostElement, type) {
|
|
2158
|
-
const
|
|
2278
|
+
const delegate = this.delegateRendererFactory.createRenderer(hostElement, type);
|
|
2159
2279
|
if (!type)
|
|
2160
|
-
return
|
|
2161
|
-
//
|
|
2162
|
-
|
|
2163
|
-
|
|
2280
|
+
return delegate;
|
|
2281
|
+
// TODO: handle html in canvas
|
|
2282
|
+
if (type['type'][HTML]) {
|
|
2283
|
+
this.rendererMap.set(type.id, delegate);
|
|
2284
|
+
return delegate;
|
|
2285
|
+
}
|
|
2164
2286
|
if (type['type'][ROUTED_SCENE]) {
|
|
2165
|
-
this
|
|
2287
|
+
this.routedSet.add(type.id);
|
|
2166
2288
|
}
|
|
2167
|
-
let renderer = this
|
|
2289
|
+
let renderer = this.rendererMap.get(type.id);
|
|
2168
2290
|
if (!renderer) {
|
|
2169
|
-
renderer = new NgtRenderer(
|
|
2291
|
+
renderer = new NgtRenderer(delegate, this.rendererStore, this.catalogue, this.zone, this.cdr,
|
|
2170
2292
|
// setting root scene if there's no routed scene OR this component is the routed Scene
|
|
2171
|
-
!hostElement && (this
|
|
2172
|
-
this
|
|
2293
|
+
!hostElement && (this.routedSet.size === 0 || this.routedSet.has(type.id)));
|
|
2294
|
+
this.rendererMap.set(type.id, renderer);
|
|
2173
2295
|
}
|
|
2174
2296
|
return renderer;
|
|
2175
2297
|
}
|
|
2176
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.
|
|
2177
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.
|
|
2298
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: NgtRendererFactory, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2299
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: NgtRendererFactory }); }
|
|
2178
2300
|
}
|
|
2179
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.
|
|
2301
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: NgtRendererFactory, decorators: [{
|
|
2180
2302
|
type: Injectable
|
|
2181
2303
|
}] });
|
|
2182
2304
|
/**
|
|
@@ -2208,7 +2330,7 @@ class NgtRenderer {
|
|
|
2208
2330
|
if (this.root) {
|
|
2209
2331
|
this.root = false;
|
|
2210
2332
|
const node = this.store.createNode('three', this.store.rootScene);
|
|
2211
|
-
node.__ngt_renderer__[
|
|
2333
|
+
node.__ngt_renderer__[NgtRendererClassId.injectorFactory] = () => getDebugNode(element).injector;
|
|
2212
2334
|
return node;
|
|
2213
2335
|
}
|
|
2214
2336
|
// handle compound
|
|
@@ -2224,7 +2346,7 @@ class NgtRenderer {
|
|
|
2224
2346
|
return this.store.createNode('three', Object.assign({ __ngt_renderer__: { rawValue: undefined } },
|
|
2225
2347
|
// NOTE: we assign this manually to a raw value node
|
|
2226
2348
|
// because we say it is a 'three' node but we're not using prepare()
|
|
2227
|
-
{ __ngt__: { isRaw: true, parent:
|
|
2349
|
+
{ __ngt__: { isRaw: true, parent: signal(null) } }));
|
|
2228
2350
|
}
|
|
2229
2351
|
const { injectedArgs, injectedParent, store } = this.store.getCreationState();
|
|
2230
2352
|
let parent = injectedParent;
|
|
@@ -2247,7 +2369,7 @@ class NgtRenderer {
|
|
|
2247
2369
|
localState.store = store;
|
|
2248
2370
|
const node = this.store.createNode('three', object);
|
|
2249
2371
|
if (parent) {
|
|
2250
|
-
node.__ngt_renderer__[
|
|
2372
|
+
node.__ngt_renderer__[NgtRendererClassId.injectedParent] = parent;
|
|
2251
2373
|
}
|
|
2252
2374
|
return node;
|
|
2253
2375
|
}
|
|
@@ -2267,7 +2389,7 @@ class NgtRenderer {
|
|
|
2267
2389
|
localState.attach = ['material'];
|
|
2268
2390
|
}
|
|
2269
2391
|
if (parent) {
|
|
2270
|
-
node.__ngt_renderer__[
|
|
2392
|
+
node.__ngt_renderer__[NgtRendererClassId.injectedParent] = parent;
|
|
2271
2393
|
}
|
|
2272
2394
|
return node;
|
|
2273
2395
|
}
|
|
@@ -2277,24 +2399,36 @@ class NgtRenderer {
|
|
|
2277
2399
|
return this.store.createNode('comment', this.delegate.createComment(value));
|
|
2278
2400
|
}
|
|
2279
2401
|
appendChild(parent, newChild) {
|
|
2280
|
-
// TODO: just ignore text node for now
|
|
2281
|
-
if (newChild instanceof Text)
|
|
2282
|
-
return;
|
|
2283
|
-
const cRS = newChild.__ngt_renderer__;
|
|
2284
2402
|
const pRS = parent.__ngt_renderer__;
|
|
2285
|
-
|
|
2403
|
+
const cRS = newChild.__ngt_renderer__;
|
|
2404
|
+
if (pRS[NgtRendererClassId.type] === 'dom' &&
|
|
2405
|
+
(newChild instanceof Text || cRS[NgtRendererClassId.type] === 'dom')) {
|
|
2406
|
+
this.store.addChild(parent, newChild);
|
|
2407
|
+
this.delegate.appendChild(parent, newChild);
|
|
2408
|
+
if (cRS) {
|
|
2409
|
+
this.store.setParent(newChild, parent);
|
|
2410
|
+
if (this.shouldFindGrandparentInstance(pRS, cRS, newChild)) {
|
|
2411
|
+
// we'll try to get the grandparent instance here so that we can run appendChild with both instances
|
|
2412
|
+
const closestGrandparentInstance = this.store.getClosestParentWithInstance(parent);
|
|
2413
|
+
if (closestGrandparentInstance)
|
|
2414
|
+
this.appendChild(closestGrandparentInstance, newChild);
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
return;
|
|
2418
|
+
}
|
|
2419
|
+
if (cRS?.[NgtRendererClassId.type] === 'comment') {
|
|
2286
2420
|
this.store.setParent(newChild, parent);
|
|
2287
2421
|
return;
|
|
2288
2422
|
}
|
|
2289
|
-
if (cRS[
|
|
2290
|
-
if (is.ref(cRS[
|
|
2291
|
-
const injector = cRS[
|
|
2423
|
+
if (cRS?.[NgtRendererClassId.injectedParent]) {
|
|
2424
|
+
if (is.ref(cRS[NgtRendererClassId.injectedParent])) {
|
|
2425
|
+
const injector = cRS[NgtRendererClassId.injectorFactory]().get(Injector, null);
|
|
2292
2426
|
if (!injector) {
|
|
2293
2427
|
console.warn(`[NGT] NgtRenderer is attempting to start an effect for injectedParent but no Injector is found.`);
|
|
2294
2428
|
return;
|
|
2295
2429
|
}
|
|
2296
2430
|
const watcher = effect(() => {
|
|
2297
|
-
const injectedParent = cRS[
|
|
2431
|
+
const injectedParent = cRS[NgtRendererClassId.injectedParent].nativeElement;
|
|
2298
2432
|
if (injectedParent && injectedParent !== parent) {
|
|
2299
2433
|
this.appendChild(injectedParent, newChild);
|
|
2300
2434
|
// only run this effect once
|
|
@@ -2304,38 +2438,38 @@ class NgtRenderer {
|
|
|
2304
2438
|
}, { injector, manualCleanup: true });
|
|
2305
2439
|
return;
|
|
2306
2440
|
}
|
|
2307
|
-
else if (parent !== cRS[
|
|
2308
|
-
this.appendChild(cRS[
|
|
2441
|
+
else if (parent !== cRS[NgtRendererClassId.injectedParent]) {
|
|
2442
|
+
this.appendChild(cRS[NgtRendererClassId.injectedParent], newChild);
|
|
2309
2443
|
return;
|
|
2310
2444
|
}
|
|
2311
2445
|
}
|
|
2312
2446
|
this.store.setParent(newChild, parent);
|
|
2313
2447
|
this.store.addChild(parent, newChild);
|
|
2314
2448
|
// if new child is a portal
|
|
2315
|
-
if (cRS[
|
|
2449
|
+
if (cRS?.[NgtRendererClassId.type] === 'portal') {
|
|
2316
2450
|
this.store.processPortalContainer(newChild);
|
|
2317
|
-
if (cRS[
|
|
2318
|
-
this.appendChild(parent, cRS[
|
|
2451
|
+
if (cRS[NgtRendererClassId.portalContainer]) {
|
|
2452
|
+
this.appendChild(parent, cRS[NgtRendererClassId.portalContainer]);
|
|
2319
2453
|
}
|
|
2320
2454
|
return;
|
|
2321
2455
|
}
|
|
2322
2456
|
// if parent is a portal
|
|
2323
|
-
if (pRS[
|
|
2457
|
+
if (pRS[NgtRendererClassId.type] === 'portal') {
|
|
2324
2458
|
this.store.processPortalContainer(parent);
|
|
2325
|
-
if (pRS[
|
|
2326
|
-
this.appendChild(pRS[
|
|
2459
|
+
if (pRS[NgtRendererClassId.portalContainer]) {
|
|
2460
|
+
this.appendChild(pRS[NgtRendererClassId.portalContainer], newChild);
|
|
2327
2461
|
}
|
|
2328
2462
|
return;
|
|
2329
2463
|
}
|
|
2330
2464
|
// if both are three instances, straightforward case
|
|
2331
|
-
if (pRS[
|
|
2465
|
+
if (pRS[NgtRendererClassId.type] === 'three' && cRS?.[NgtRendererClassId.type] === 'three') {
|
|
2332
2466
|
// if child already attached to a parent, skip
|
|
2333
2467
|
if (getLocalState(newChild).parent && untracked(getLocalState(newChild).parent))
|
|
2334
2468
|
return;
|
|
2335
2469
|
// attach THREE child
|
|
2336
2470
|
attachThreeChild(parent, newChild);
|
|
2337
2471
|
// here, we handle the special case of if the parent has a compoundParent, which means this child is part of a compound parent template
|
|
2338
|
-
if (!cRS[
|
|
2472
|
+
if (!cRS[NgtRendererClassId.compound])
|
|
2339
2473
|
return;
|
|
2340
2474
|
const closestGrandparentWithCompound = this.store.getClosestParentWithCompound(parent);
|
|
2341
2475
|
if (!closestGrandparentWithCompound)
|
|
@@ -2344,51 +2478,39 @@ class NgtRenderer {
|
|
|
2344
2478
|
return;
|
|
2345
2479
|
}
|
|
2346
2480
|
// if only the parent is the THREE instance
|
|
2347
|
-
if (pRS[
|
|
2348
|
-
for (const renderChild of cRS[
|
|
2481
|
+
if (pRS[NgtRendererClassId.type] === 'three') {
|
|
2482
|
+
for (const renderChild of cRS?.[NgtRendererClassId.children]) {
|
|
2349
2483
|
this.appendChild(parent, renderChild);
|
|
2350
2484
|
}
|
|
2351
2485
|
}
|
|
2352
2486
|
// if parent is a compound
|
|
2353
|
-
if (pRS[
|
|
2487
|
+
if (pRS[NgtRendererClassId.type] === 'compound') {
|
|
2354
2488
|
// if compound doesn't have a THREE instance set yet
|
|
2355
|
-
if (!pRS[
|
|
2489
|
+
if (!pRS[NgtRendererClassId.compounded] && cRS[NgtRendererClassId.type] === 'three') {
|
|
2356
2490
|
// if child is indeed an ngtCompound
|
|
2357
|
-
if (cRS[
|
|
2491
|
+
if (cRS[NgtRendererClassId.compound])
|
|
2358
2492
|
this.store.setCompound(parent, newChild);
|
|
2359
2493
|
// if not, we track the parent (that is supposedly the compound component) on this three instance
|
|
2360
|
-
else if (!cRS[
|
|
2361
|
-
cRS[
|
|
2494
|
+
else if (!cRS[NgtRendererClassId.compoundParent])
|
|
2495
|
+
cRS[NgtRendererClassId.compoundParent] = parent;
|
|
2362
2496
|
}
|
|
2363
2497
|
// reset the compound if it's changed
|
|
2364
|
-
if (pRS[
|
|
2365
|
-
cRS[
|
|
2366
|
-
cRS[
|
|
2367
|
-
pRS[
|
|
2498
|
+
if (pRS[NgtRendererClassId.compounded] &&
|
|
2499
|
+
cRS[NgtRendererClassId.type] === 'three' &&
|
|
2500
|
+
cRS[NgtRendererClassId.compound] &&
|
|
2501
|
+
pRS[NgtRendererClassId.compounded] !== newChild) {
|
|
2368
2502
|
this.store.setCompound(parent, newChild);
|
|
2369
2503
|
}
|
|
2370
2504
|
}
|
|
2371
|
-
|
|
2372
|
-
// if child is three but haven't been attached to a parent yet
|
|
2373
|
-
(cRS[0 /* NgtRendererClassId.type */] === 'three' && !untracked(getLocalState(newChild).parent)) ||
|
|
2374
|
-
// or both parent and child are DOM elements
|
|
2375
|
-
// or they are compound AND haven't had a THREE instance yet
|
|
2376
|
-
((pRS[0 /* NgtRendererClassId.type */] === 'dom' ||
|
|
2377
|
-
(pRS[0 /* NgtRendererClassId.type */] === 'compound' && !pRS[7 /* NgtRendererClassId.compounded */])) &&
|
|
2378
|
-
(cRS[0 /* NgtRendererClassId.type */] === 'dom' ||
|
|
2379
|
-
(cRS[0 /* NgtRendererClassId.type */] === 'compound' && !cRS[7 /* NgtRendererClassId.compounded */])));
|
|
2380
|
-
if (shouldFindGrandparentInstance) {
|
|
2505
|
+
if (this.shouldFindGrandparentInstance(pRS, cRS, newChild)) {
|
|
2381
2506
|
// we'll try to get the grandparent instance here so that we can run appendChild with both instances
|
|
2382
2507
|
const closestGrandparentInstance = this.store.getClosestParentWithInstance(parent);
|
|
2383
2508
|
if (closestGrandparentInstance)
|
|
2384
2509
|
this.appendChild(closestGrandparentInstance, newChild);
|
|
2510
|
+
return;
|
|
2385
2511
|
}
|
|
2386
2512
|
}
|
|
2387
|
-
insertBefore(parent, newChild
|
|
2388
|
-
// TODO: we might need these?
|
|
2389
|
-
// refChild: NgtRendererNode
|
|
2390
|
-
// isMove?: boolean | undefined
|
|
2391
|
-
) {
|
|
2513
|
+
insertBefore(parent, newChild) {
|
|
2392
2514
|
if (parent == null || !parent.__ngt_renderer__ || parent === newChild)
|
|
2393
2515
|
return;
|
|
2394
2516
|
this.appendChild(parent, newChild);
|
|
@@ -2396,16 +2518,28 @@ class NgtRenderer {
|
|
|
2396
2518
|
removeChild(parent, oldChild, isHostElement) {
|
|
2397
2519
|
const pRS = parent.__ngt_renderer__;
|
|
2398
2520
|
const cRS = oldChild.__ngt_renderer__;
|
|
2399
|
-
if (
|
|
2521
|
+
if ((!cRS || !pRS) &&
|
|
2522
|
+
parent instanceof Element &&
|
|
2523
|
+
(oldChild instanceof Element || oldChild instanceof Text || oldChild instanceof Comment)) {
|
|
2524
|
+
this.delegate.removeChild(parent, oldChild);
|
|
2525
|
+
this.store.destroy(oldChild, parent);
|
|
2526
|
+
return;
|
|
2527
|
+
}
|
|
2528
|
+
if (cRS[NgtRendererClassId.type] === 'dom' && (!pRS || pRS[NgtRendererClassId.type] === 'dom')) {
|
|
2529
|
+
this.delegate.removeChild(parent, oldChild);
|
|
2530
|
+
this.store.destroy(oldChild, parent);
|
|
2531
|
+
return;
|
|
2532
|
+
}
|
|
2533
|
+
if (pRS[NgtRendererClassId.type] === 'three' && cRS[NgtRendererClassId.type] === 'three') {
|
|
2400
2534
|
removeThreeChild(parent, oldChild, true);
|
|
2401
2535
|
this.store.destroy(oldChild, parent);
|
|
2402
2536
|
return;
|
|
2403
2537
|
}
|
|
2404
|
-
if (pRS[
|
|
2405
|
-
this.removeChild(pRS[
|
|
2538
|
+
if (pRS[NgtRendererClassId.type] === 'compound' && pRS[NgtRendererClassId.parent]) {
|
|
2539
|
+
this.removeChild(pRS[NgtRendererClassId.parent], oldChild, isHostElement);
|
|
2406
2540
|
return;
|
|
2407
2541
|
}
|
|
2408
|
-
if (pRS[
|
|
2542
|
+
if (pRS[NgtRendererClassId.type] === 'three') {
|
|
2409
2543
|
this.store.destroy(oldChild, parent);
|
|
2410
2544
|
return;
|
|
2411
2545
|
}
|
|
@@ -2416,44 +2550,49 @@ class NgtRenderer {
|
|
|
2416
2550
|
}
|
|
2417
2551
|
parentNode(node) {
|
|
2418
2552
|
const rS = node.__ngt_renderer__;
|
|
2419
|
-
if (rS?.[
|
|
2420
|
-
return rS[
|
|
2553
|
+
if (rS?.[NgtRendererClassId.parent])
|
|
2554
|
+
return rS[NgtRendererClassId.parent];
|
|
2421
2555
|
return this.delegate.parentNode(node);
|
|
2422
2556
|
}
|
|
2423
2557
|
setAttribute(el, name, value, namespace) {
|
|
2424
2558
|
const rS = el.__ngt_renderer__;
|
|
2425
|
-
if (rS[
|
|
2559
|
+
if (rS[NgtRendererClassId.type] === 'compound') {
|
|
2426
2560
|
// we don't have the compound instance yet
|
|
2427
|
-
rS[
|
|
2428
|
-
if (!rS[
|
|
2561
|
+
rS[NgtRendererClassId.attributes][name] = value;
|
|
2562
|
+
if (!rS[NgtRendererClassId.compounded]) {
|
|
2429
2563
|
this.store.queueOperation(el, ['op', () => this.setAttribute(el, name, value, namespace)]);
|
|
2430
2564
|
return;
|
|
2431
2565
|
}
|
|
2432
|
-
this.setAttribute(rS[
|
|
2566
|
+
this.setAttribute(rS[NgtRendererClassId.compounded], name, value, namespace);
|
|
2433
2567
|
return;
|
|
2434
2568
|
}
|
|
2435
|
-
if (rS[
|
|
2569
|
+
if (rS[NgtRendererClassId.type] === 'three') {
|
|
2436
2570
|
this.store.applyAttribute(el, name, value);
|
|
2571
|
+
return;
|
|
2572
|
+
}
|
|
2573
|
+
return this.delegate.setAttribute(el, name, value);
|
|
2437
2574
|
}
|
|
2438
2575
|
setProperty(el, name, value) {
|
|
2576
|
+
// TODO: should we support ref value
|
|
2439
2577
|
const rS = el.__ngt_renderer__;
|
|
2440
|
-
if (rS[
|
|
2578
|
+
if (rS[NgtRendererClassId.type] === 'compound') {
|
|
2441
2579
|
// we don't have the compound instance yet
|
|
2442
|
-
rS[
|
|
2443
|
-
if (!rS[
|
|
2580
|
+
rS[NgtRendererClassId.properties][name] = value;
|
|
2581
|
+
if (!rS[NgtRendererClassId.compounded]) {
|
|
2444
2582
|
this.store.queueOperation(el, ['op', () => this.setProperty(el, name, value)]);
|
|
2445
2583
|
return;
|
|
2446
2584
|
}
|
|
2447
|
-
if (rS[
|
|
2448
|
-
Object.assign(rS[
|
|
2449
|
-
props: Object.assign(rS[7 /* NgtRendererClassId.compounded */].__ngt_renderer__[5 /* NgtRendererClassId.compound */], { [name]: value }),
|
|
2450
|
-
});
|
|
2585
|
+
if (rS[NgtRendererClassId.compounded].__ngt_renderer__[NgtRendererClassId.compound]) {
|
|
2586
|
+
Object.assign(rS[NgtRendererClassId.compounded].__ngt_renderer__[NgtRendererClassId.compound][NgtCompoundClassId.props], { [name]: value });
|
|
2451
2587
|
}
|
|
2452
|
-
this.setProperty(rS[
|
|
2588
|
+
this.setProperty(rS[NgtRendererClassId.compounded], name, value);
|
|
2453
2589
|
return;
|
|
2454
2590
|
}
|
|
2455
|
-
if (rS[
|
|
2591
|
+
if (rS[NgtRendererClassId.type] === 'three') {
|
|
2456
2592
|
this.store.applyProperty(el, name, value);
|
|
2593
|
+
return;
|
|
2594
|
+
}
|
|
2595
|
+
return this.delegate.setProperty(el, name, value);
|
|
2457
2596
|
}
|
|
2458
2597
|
listen(target, eventName, callback) {
|
|
2459
2598
|
const rS = target.__ngt_renderer__;
|
|
@@ -2462,15 +2601,15 @@ class NgtRenderer {
|
|
|
2462
2601
|
if (!rS || this.store.isDOM(target)) {
|
|
2463
2602
|
return this.delegate.listen(target, eventName, callback);
|
|
2464
2603
|
}
|
|
2465
|
-
if (rS[
|
|
2466
|
-
(rS[
|
|
2467
|
-
const instance = rS[
|
|
2604
|
+
if (rS[NgtRendererClassId.type] === 'three' ||
|
|
2605
|
+
(rS[NgtRendererClassId.type] === 'compound' && rS[NgtRendererClassId.compounded])) {
|
|
2606
|
+
const instance = rS[NgtRendererClassId.compounded] || target;
|
|
2468
2607
|
const priority = getLocalState(target).priority;
|
|
2469
|
-
const targetCdr = rS[
|
|
2470
|
-
rS[
|
|
2608
|
+
const targetCdr = rS[NgtRendererClassId.injectorFactory]?.().get(ChangeDetectorRef, null) ||
|
|
2609
|
+
rS[NgtRendererClassId.parent]?.__ngt_renderer__?.[NgtRendererClassId.injectorFactory]?.().get(ChangeDetectorRef, null);
|
|
2471
2610
|
return processThreeEvent(instance, priority || 0, eventName, callback, this.zone, this.cdr, targetCdr);
|
|
2472
2611
|
}
|
|
2473
|
-
if (rS[
|
|
2612
|
+
if (rS[NgtRendererClassId.type] === 'compound' && !rS[NgtRendererClassId.compounded]) {
|
|
2474
2613
|
this.store.queueOperation(target, [
|
|
2475
2614
|
'op',
|
|
2476
2615
|
() => this.store.queueOperation(target, ['cleanUp', this.listen(target, eventName, callback)]),
|
|
@@ -2491,170 +2630,165 @@ class NgtRenderer {
|
|
|
2491
2630
|
}
|
|
2492
2631
|
return () => { };
|
|
2493
2632
|
}
|
|
2633
|
+
shouldFindGrandparentInstance(pRS, cRS, child) {
|
|
2634
|
+
const pType = pRS[NgtRendererClassId.type];
|
|
2635
|
+
const cType = cRS[NgtRendererClassId.type];
|
|
2636
|
+
const isParentCompounded = pRS[NgtRendererClassId.compounded];
|
|
2637
|
+
const isChildCompounded = cRS[NgtRendererClassId.compounded];
|
|
2638
|
+
// if child is three but haven't been attached to a parent yet
|
|
2639
|
+
const isDanglingThreeChild = cType === 'three' && !untracked(getLocalState(child).parent);
|
|
2640
|
+
// or both parent and child are DOM elements
|
|
2641
|
+
// or they are compound AND haven't had a THREE instance yet
|
|
2642
|
+
const isParentStillDOM = pType === 'dom' || (pType === 'compound' && !isParentCompounded);
|
|
2643
|
+
const isChildStillDOM = cType === 'dom' || (cType === 'compound' && !isChildCompounded);
|
|
2644
|
+
// and the child is a compounded compound
|
|
2645
|
+
const isCompoundChildCompounded = cType === 'compound' && !!isChildCompounded;
|
|
2646
|
+
return (isDanglingThreeChild ||
|
|
2647
|
+
(isParentStillDOM && isChildStillDOM) ||
|
|
2648
|
+
(isParentStillDOM && isCompoundChildCompounded));
|
|
2649
|
+
}
|
|
2494
2650
|
get data() {
|
|
2495
2651
|
return this.delegate.data;
|
|
2496
2652
|
}
|
|
2497
2653
|
}
|
|
2498
|
-
|
|
2499
|
-
function provideNgtRenderer({ store, changeDetectorRef, compoundPrefixes = [] }) {
|
|
2654
|
+
function provideNgtRenderer(store, compoundPrefixes, cdr) {
|
|
2500
2655
|
if (!compoundPrefixes.includes('ngts'))
|
|
2501
2656
|
compoundPrefixes.push('ngts');
|
|
2502
2657
|
if (!compoundPrefixes.includes('ngtp'))
|
|
2503
2658
|
compoundPrefixes.push('ngtp');
|
|
2504
2659
|
return makeEnvironmentProviders([
|
|
2505
2660
|
{ provide: RendererFactory2, useClass: NgtRendererFactory },
|
|
2506
|
-
{ provide: NgtStore, useValue: store },
|
|
2507
|
-
{ provide: ChangeDetectorRef, useValue: changeDetectorRef },
|
|
2508
2661
|
{ provide: NGT_COMPOUND_PREFIXES, useValue: compoundPrefixes },
|
|
2662
|
+
{ provide: ChangeDetectorRef, useValue: cdr },
|
|
2663
|
+
provideNgtStore(store),
|
|
2509
2664
|
provideZoneChangeDetection({ runCoalescing: true, eventCoalescing: true }),
|
|
2510
2665
|
]);
|
|
2511
2666
|
}
|
|
2512
2667
|
|
|
2513
|
-
class NgtCanvas
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2668
|
+
class NgtCanvas {
|
|
2669
|
+
constructor() {
|
|
2670
|
+
this.store = injectNgtStore();
|
|
2671
|
+
this.initRoot = injectCanvasRootInitializer();
|
|
2672
|
+
this.host = inject(ElementRef);
|
|
2673
|
+
this.viewContainerRef = inject(ViewContainerRef);
|
|
2674
|
+
this.injector = inject(Injector);
|
|
2675
|
+
this.environmentInjector = inject(EnvironmentInjector);
|
|
2676
|
+
this.zone = inject(NgZone);
|
|
2677
|
+
this.destroyRef = inject(DestroyRef);
|
|
2678
|
+
this.cdr = inject(ChangeDetectorRef);
|
|
2679
|
+
this.inputs = signalStore({
|
|
2680
|
+
shadows: false,
|
|
2681
|
+
linear: false,
|
|
2682
|
+
flat: false,
|
|
2683
|
+
legacy: false,
|
|
2684
|
+
orthographic: false,
|
|
2685
|
+
frameloop: 'always',
|
|
2686
|
+
dpr: [1, 2],
|
|
2687
|
+
events: createPointerEvents,
|
|
2688
|
+
});
|
|
2689
|
+
this.sceneGraphInputs = {};
|
|
2690
|
+
this.compoundPrefixes = [];
|
|
2691
|
+
this.created = new EventEmitter();
|
|
2692
|
+
this.inputsEventSource = this.inputs.select('eventSource');
|
|
2693
|
+
this.hbPointerEvents = computed(() => (!!this.inputsEventSource() ? 'none' : 'auto'));
|
|
2694
|
+
}
|
|
2521
2695
|
set linear(linear) {
|
|
2522
|
-
this.set({ linear });
|
|
2696
|
+
this.inputs.set({ linear });
|
|
2523
2697
|
}
|
|
2524
2698
|
set legacy(legacy) {
|
|
2525
|
-
this.set({ legacy });
|
|
2699
|
+
this.inputs.set({ legacy });
|
|
2526
2700
|
}
|
|
2527
2701
|
set flat(flat) {
|
|
2528
|
-
this.set({ flat });
|
|
2702
|
+
this.inputs.set({ flat });
|
|
2529
2703
|
}
|
|
2530
2704
|
set orthographic(orthographic) {
|
|
2531
|
-
this.set({ orthographic });
|
|
2705
|
+
this.inputs.set({ orthographic });
|
|
2532
2706
|
}
|
|
2533
2707
|
set frameloop(frameloop) {
|
|
2534
|
-
this.set({ frameloop });
|
|
2708
|
+
this.inputs.set({ frameloop });
|
|
2535
2709
|
}
|
|
2536
2710
|
set dpr(dpr) {
|
|
2537
|
-
this.set({ dpr });
|
|
2711
|
+
this.inputs.set({ dpr });
|
|
2538
2712
|
}
|
|
2539
2713
|
set raycaster(raycaster) {
|
|
2540
|
-
this.set({ raycaster });
|
|
2714
|
+
this.inputs.set({ raycaster });
|
|
2541
2715
|
}
|
|
2542
2716
|
set shadows(shadows) {
|
|
2543
|
-
this.set({ shadows });
|
|
2717
|
+
this.inputs.set({ shadows });
|
|
2544
2718
|
}
|
|
2545
2719
|
set camera(camera) {
|
|
2546
|
-
this.set({ camera });
|
|
2720
|
+
this.inputs.set({ camera });
|
|
2547
2721
|
}
|
|
2548
2722
|
set scene(scene) {
|
|
2549
|
-
this.set({ scene });
|
|
2723
|
+
this.inputs.set({ scene });
|
|
2550
2724
|
}
|
|
2551
2725
|
set gl(gl) {
|
|
2552
|
-
this.set({ gl });
|
|
2726
|
+
this.inputs.set({ gl });
|
|
2553
2727
|
}
|
|
2554
2728
|
set eventSource(eventSource) {
|
|
2555
|
-
this.set({ eventSource });
|
|
2729
|
+
this.inputs.set({ eventSource });
|
|
2556
2730
|
}
|
|
2557
2731
|
set eventPrefix(eventPrefix) {
|
|
2558
|
-
this.set({ eventPrefix });
|
|
2732
|
+
this.inputs.set({ eventPrefix });
|
|
2559
2733
|
}
|
|
2560
2734
|
set lookAt(lookAt) {
|
|
2561
|
-
this.set({ lookAt });
|
|
2735
|
+
this.inputs.set({ lookAt });
|
|
2562
2736
|
}
|
|
2563
2737
|
set performance(performance) {
|
|
2564
|
-
this.set({ performance });
|
|
2565
|
-
}
|
|
2566
|
-
#glRef;
|
|
2567
|
-
#glEnvInjector;
|
|
2568
|
-
constructor() {
|
|
2569
|
-
super({
|
|
2570
|
-
shadows: false,
|
|
2571
|
-
linear: false,
|
|
2572
|
-
flat: false,
|
|
2573
|
-
legacy: false,
|
|
2574
|
-
orthographic: false,
|
|
2575
|
-
frameloop: 'always',
|
|
2576
|
-
dpr: [1, 2],
|
|
2577
|
-
events: createPointerEvents,
|
|
2578
|
-
});
|
|
2579
|
-
this.#envInjector = inject(EnvironmentInjector);
|
|
2580
|
-
this.#injector = inject(Injector);
|
|
2581
|
-
this.#host = inject(ElementRef);
|
|
2582
|
-
this.#zone = inject(NgZone);
|
|
2583
|
-
this.#cdr = inject(ChangeDetectorRef);
|
|
2584
|
-
this.#store = inject(NgtStore);
|
|
2585
|
-
this.#isReady = this.#store.select('ready');
|
|
2586
|
-
this.sceneGraphInputs = {};
|
|
2587
|
-
this.compoundPrefixes = [];
|
|
2588
|
-
this.created = new EventEmitter();
|
|
2589
|
-
this.pointerMissed = new EventEmitter();
|
|
2590
|
-
inject(DestroyRef).onDestroy(() => {
|
|
2591
|
-
if (this.#glRef)
|
|
2592
|
-
this.#glRef.destroy();
|
|
2593
|
-
if (this.#glEnvInjector)
|
|
2594
|
-
this.#glEnvInjector.destroy();
|
|
2595
|
-
injectNgtLoader.destroy();
|
|
2596
|
-
this.#store.destroy(this.glCanvas.nativeElement);
|
|
2597
|
-
});
|
|
2598
|
-
}
|
|
2599
|
-
get hbPointerEvents() {
|
|
2600
|
-
return this.select('eventSource')() !== this.#host.nativeElement ? 'none' : 'auto';
|
|
2738
|
+
this.inputs.set({ performance });
|
|
2601
2739
|
}
|
|
2602
2740
|
ngOnChanges(changes) {
|
|
2603
|
-
if (changes['sceneGraphInputs'] && !changes['sceneGraphInputs'].firstChange && this
|
|
2604
|
-
this
|
|
2741
|
+
if (changes['sceneGraphInputs'] && !changes['sceneGraphInputs'].firstChange && this.glRef) {
|
|
2742
|
+
this.setSceneGraphInputs();
|
|
2605
2743
|
}
|
|
2606
2744
|
}
|
|
2607
2745
|
ngOnInit() {
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
this
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
this
|
|
2626
|
-
|
|
2627
|
-
this
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
this.#resizeRef.destroy();
|
|
2637
|
-
}
|
|
2638
|
-
if (width > 0 && height > 0) {
|
|
2639
|
-
if (!this.#store.isInit)
|
|
2640
|
-
this.#store.init();
|
|
2641
|
-
this.#resizeRef = this.#zone.run(() => effect(() => {
|
|
2642
|
-
const canvasInputs = this.state();
|
|
2643
|
-
this.#zone.runOutsideAngular(() => {
|
|
2644
|
-
this.#store.configure({ ...canvasInputs, size: { width, height, top, left } }, this.glCanvas.nativeElement);
|
|
2746
|
+
// NOTE: we resolve glCanvas at this point, setup the configurator
|
|
2747
|
+
this.configurator = this.initRoot(this.glCanvas.nativeElement);
|
|
2748
|
+
this.destroyRef.onDestroy(() => {
|
|
2749
|
+
this.glEnvironmentInjector?.destroy();
|
|
2750
|
+
this.glRef?.destroy();
|
|
2751
|
+
this.resizeEffectRef?.destroy();
|
|
2752
|
+
injectNgtLoader.destroy();
|
|
2753
|
+
this.configurator?.destroy();
|
|
2754
|
+
});
|
|
2755
|
+
}
|
|
2756
|
+
// NOTE: runs outside of Zone due to emitInZone: false
|
|
2757
|
+
onResize(result) {
|
|
2758
|
+
if (result.width > 0 && result.height > 0) {
|
|
2759
|
+
this.resizeEffectRef?.destroy();
|
|
2760
|
+
const inputs = this.inputs.select();
|
|
2761
|
+
// NOTE: go back into zone so that effect runs
|
|
2762
|
+
// TODO: Double-check when effect is made not depended on zone
|
|
2763
|
+
this.resizeEffectRef = this.zone.run(() => effect(() => {
|
|
2764
|
+
this.zone.runOutsideAngular(() => {
|
|
2765
|
+
if (!this.configurator)
|
|
2766
|
+
this.configurator = this.initRoot(this.glCanvas.nativeElement);
|
|
2767
|
+
this.configurator.configure({ ...inputs(), size: result });
|
|
2768
|
+
if (this.glRef) {
|
|
2769
|
+
this.cdr.detectChanges();
|
|
2770
|
+
}
|
|
2771
|
+
else {
|
|
2772
|
+
this.render();
|
|
2773
|
+
}
|
|
2645
2774
|
});
|
|
2646
|
-
}, {
|
|
2775
|
+
}, { manualCleanup: true, injector: this.injector }));
|
|
2647
2776
|
}
|
|
2648
2777
|
}
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
const
|
|
2778
|
+
render() {
|
|
2779
|
+
this.glEnvironmentInjector?.destroy();
|
|
2780
|
+
this.glRef?.destroy();
|
|
2781
|
+
// Flag the canvas active, rendering will now begin
|
|
2782
|
+
this.store.set((state) => ({ internal: { ...state.internal, active: true } }));
|
|
2783
|
+
const inputs = this.inputs.get();
|
|
2784
|
+
const state = this.store.get();
|
|
2655
2785
|
// connect to event source
|
|
2656
|
-
state.events.connect?.(
|
|
2657
|
-
|
|
2786
|
+
state.events.connect?.(inputs.eventSource
|
|
2787
|
+
? is.ref(inputs.eventSource)
|
|
2788
|
+
? inputs.eventSource.nativeElement
|
|
2789
|
+
: inputs.eventSource
|
|
2790
|
+
: this.host.nativeElement);
|
|
2791
|
+
// setup compute for eventPrefix
|
|
2658
2792
|
if (inputs.eventPrefix) {
|
|
2659
2793
|
state.setEvents({
|
|
2660
2794
|
compute: (event, store) => {
|
|
@@ -2668,70 +2802,71 @@ class NgtCanvas extends NgtSignalStore {
|
|
|
2668
2802
|
}
|
|
2669
2803
|
// emit created event if observed
|
|
2670
2804
|
if (this.created.observed) {
|
|
2671
|
-
// but go back into zone to
|
|
2672
|
-
this
|
|
2673
|
-
this.created.emit(this
|
|
2805
|
+
// but go back into zone to do so
|
|
2806
|
+
this.zone.run(() => {
|
|
2807
|
+
this.created.emit(this.store.get());
|
|
2674
2808
|
});
|
|
2675
2809
|
}
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
changeDetectorRef: this.#cdr,
|
|
2684
|
-
compoundPrefixes: this.compoundPrefixes,
|
|
2685
|
-
}),
|
|
2686
|
-
], this.#envInjector);
|
|
2687
|
-
this.#glRef = this.glAnchor.createComponent(this.sceneGraph, {
|
|
2688
|
-
environmentInjector: this.#glEnvInjector,
|
|
2689
|
-
});
|
|
2690
|
-
this.#setSceneGraphInputs();
|
|
2691
|
-
this.#overrideChangeDetectorRef();
|
|
2692
|
-
safeDetectChanges(this.#cdr);
|
|
2810
|
+
if (!this.store.get('events', 'connected')) {
|
|
2811
|
+
this.store.get('events').connect?.(this.glCanvas.nativeElement);
|
|
2812
|
+
}
|
|
2813
|
+
this.glEnvironmentInjector = createEnvironmentInjector([provideNgtRenderer(this.store, this.compoundPrefixes, this.cdr)], this.environmentInjector);
|
|
2814
|
+
this.glRef = this.viewContainerRef.createComponent(this.sceneGraph, {
|
|
2815
|
+
environmentInjector: this.glEnvironmentInjector,
|
|
2816
|
+
injector: this.injector,
|
|
2693
2817
|
});
|
|
2818
|
+
this.overrideChangeDetectorRef();
|
|
2819
|
+
this.setSceneGraphInputs();
|
|
2694
2820
|
}
|
|
2695
|
-
|
|
2696
|
-
const originalDetectChanges = this
|
|
2697
|
-
this
|
|
2821
|
+
overrideChangeDetectorRef() {
|
|
2822
|
+
const originalDetectChanges = this.cdr.detectChanges.bind(this.cdr);
|
|
2823
|
+
this.cdr.detectChanges = () => {
|
|
2698
2824
|
originalDetectChanges();
|
|
2699
|
-
safeDetectChanges(this
|
|
2825
|
+
safeDetectChanges(this.glRef?.changeDetectorRef);
|
|
2700
2826
|
};
|
|
2701
2827
|
}
|
|
2702
|
-
|
|
2703
|
-
this
|
|
2704
|
-
if (this
|
|
2828
|
+
setSceneGraphInputs() {
|
|
2829
|
+
this.zone.run(() => {
|
|
2830
|
+
if (this.glRef) {
|
|
2705
2831
|
for (const [key, value] of Object.entries(this.sceneGraphInputs)) {
|
|
2706
|
-
this
|
|
2832
|
+
this.glRef.setInput(key, value);
|
|
2707
2833
|
}
|
|
2708
|
-
|
|
2834
|
+
this.glRef.changeDetectorRef.detectChanges();
|
|
2709
2835
|
}
|
|
2710
2836
|
});
|
|
2711
2837
|
}
|
|
2712
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.
|
|
2713
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2838
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: NgtCanvas, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2839
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.3", type: NgtCanvas, isStandalone: true, selector: "ngt-canvas", inputs: { sceneGraph: "sceneGraph", sceneGraphInputs: "sceneGraphInputs", compoundPrefixes: "compoundPrefixes", linear: "linear", legacy: "legacy", flat: "flat", orthographic: "orthographic", frameloop: "frameloop", dpr: "dpr", raycaster: "raycaster", shadows: "shadows", camera: "camera", scene: "scene", gl: "gl", eventSource: "eventSource", eventPrefix: "eventPrefix", lookAt: "lookAt", performance: "performance" }, outputs: { created: "created" }, host: { properties: { "style.pointerEvents": "hbPointerEvents()" }, styleAttribute: "display: block;position: relative;width: 100%;height: 100%;overflow: hidden;" }, providers: [
|
|
2840
|
+
provideResizeOptions({ emitInZone: false, emitInitialResult: true }),
|
|
2841
|
+
provideNgtStore(),
|
|
2842
|
+
], viewQueries: [{ propertyName: "glCanvas", first: true, predicate: ["glCanvas"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: `
|
|
2843
|
+
<div (ngxResize)="onResize($event)" style="height: 100%; width: 100%;">
|
|
2844
|
+
<canvas #glCanvas style="display: block;"></canvas>
|
|
2845
|
+
</div>
|
|
2846
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: NgxResize, selector: "[ngxResize]", inputs: ["ngxResizeOptions"], outputs: ["ngxResize"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2718
2847
|
}
|
|
2719
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.
|
|
2848
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: NgtCanvas, decorators: [{
|
|
2720
2849
|
type: Component,
|
|
2721
2850
|
args: [{
|
|
2722
2851
|
selector: 'ngt-canvas',
|
|
2723
2852
|
standalone: true,
|
|
2724
2853
|
template: `
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2854
|
+
<div (ngxResize)="onResize($event)" style="height: 100%; width: 100%;">
|
|
2855
|
+
<canvas #glCanvas style="display: block;"></canvas>
|
|
2856
|
+
</div>
|
|
2857
|
+
`,
|
|
2729
2858
|
imports: [NgxResize],
|
|
2730
|
-
providers: [
|
|
2731
|
-
|
|
2859
|
+
providers: [
|
|
2860
|
+
provideResizeOptions({ emitInZone: false, emitInitialResult: true }),
|
|
2861
|
+
provideNgtStore(),
|
|
2862
|
+
],
|
|
2863
|
+
host: {
|
|
2864
|
+
style: 'display: block;position: relative;width: 100%;height: 100%;overflow: hidden;',
|
|
2865
|
+
'[style.pointerEvents]': 'hbPointerEvents()',
|
|
2866
|
+
},
|
|
2732
2867
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
2733
2868
|
}]
|
|
2734
|
-
}],
|
|
2869
|
+
}], propDecorators: { sceneGraph: [{
|
|
2735
2870
|
type: Input,
|
|
2736
2871
|
args: [{ required: true }]
|
|
2737
2872
|
}], sceneGraphInputs: [{
|
|
@@ -2770,40 +2905,49 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImpor
|
|
|
2770
2905
|
type: Input
|
|
2771
2906
|
}], created: [{
|
|
2772
2907
|
type: Output
|
|
2773
|
-
}], pointerMissed: [{
|
|
2774
|
-
type: Output
|
|
2775
2908
|
}], glCanvas: [{
|
|
2776
2909
|
type: ViewChild,
|
|
2777
2910
|
args: ['glCanvas', { static: true }]
|
|
2778
|
-
}], glAnchor: [{
|
|
2779
|
-
type: ViewChild,
|
|
2780
|
-
args: ['glCanvas', { static: true, read: ViewContainerRef }]
|
|
2781
2911
|
}] } });
|
|
2782
2912
|
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2913
|
+
class NgtKey extends NgtCommonDirective {
|
|
2914
|
+
constructor() {
|
|
2915
|
+
super(...arguments);
|
|
2916
|
+
this.lastKey = '';
|
|
2917
|
+
}
|
|
2918
|
+
static { this.processComment = false; }
|
|
2919
|
+
validate() {
|
|
2920
|
+
return false;
|
|
2921
|
+
}
|
|
2922
|
+
set key(key) {
|
|
2923
|
+
const normalizedKey = JSON.stringify(key);
|
|
2924
|
+
if (this.lastKey !== normalizedKey) {
|
|
2925
|
+
this.lastKey = normalizedKey;
|
|
2926
|
+
this.createView();
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: NgtKey, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2930
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.3", type: NgtKey, isStandalone: true, selector: "ng-template[key]", inputs: { key: "key" }, usesInheritance: true, ngImport: i0 }); }
|
|
2791
2931
|
}
|
|
2932
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: NgtKey, decorators: [{
|
|
2933
|
+
type: Directive,
|
|
2934
|
+
args: [{ selector: 'ng-template[key]', standalone: true }]
|
|
2935
|
+
}], propDecorators: { key: [{
|
|
2936
|
+
type: Input
|
|
2937
|
+
}] } });
|
|
2792
2938
|
|
|
2793
2939
|
function injectNgtRef(initial = null, injector) {
|
|
2794
|
-
injector =
|
|
2940
|
+
injector = assertInjector(injectNgtRef, injector);
|
|
2941
|
+
const ref = is.ref(initial) ? initial : new ElementRef(initial);
|
|
2942
|
+
const refSignal = signal(ref.nativeElement);
|
|
2943
|
+
const readonlyRef = refSignal.asReadonly();
|
|
2944
|
+
const computedCached = new Map();
|
|
2795
2945
|
return runInInjectionContext(injector, () => {
|
|
2796
|
-
|
|
2797
|
-
const appRef = inject(ApplicationRef);
|
|
2798
|
-
const ref = is.ref(initial) ? initial : new ElementRef(initial);
|
|
2799
|
-
const signalRef = createSignal(ref.nativeElement);
|
|
2800
|
-
const readonlySignal = signalRef.asReadonly();
|
|
2801
|
-
const cached = new Map();
|
|
2802
|
-
inject(DestroyRef).onDestroy(() => void cached.clear());
|
|
2946
|
+
inject(DestroyRef).onDestroy(() => void computedCached.clear());
|
|
2803
2947
|
const children = (type = 'objects') => {
|
|
2804
|
-
if (!
|
|
2805
|
-
|
|
2806
|
-
const instance =
|
|
2948
|
+
if (!computedCached.has(type)) {
|
|
2949
|
+
computedCached.set(type, computed(() => {
|
|
2950
|
+
const instance = readonlyRef();
|
|
2807
2951
|
if (!instance)
|
|
2808
2952
|
return [];
|
|
2809
2953
|
const localState = getLocalState(instance);
|
|
@@ -2816,37 +2960,26 @@ function injectNgtRef(initial = null, injector) {
|
|
|
2816
2960
|
return [...localState.objects(), ...localState.nonObjects()];
|
|
2817
2961
|
}));
|
|
2818
2962
|
}
|
|
2819
|
-
return
|
|
2963
|
+
return computedCached.get(type);
|
|
2820
2964
|
};
|
|
2821
|
-
Object.
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2965
|
+
Object.defineProperties(ref, {
|
|
2966
|
+
nativeElement: {
|
|
2967
|
+
set: (newElement) => {
|
|
2968
|
+
untracked(() => {
|
|
2969
|
+
if (newElement !== refSignal()) {
|
|
2970
|
+
refSignal.set(newElement);
|
|
2971
|
+
}
|
|
2972
|
+
});
|
|
2973
|
+
},
|
|
2974
|
+
get: readonlyRef,
|
|
2828
2975
|
},
|
|
2829
|
-
get: () =>
|
|
2976
|
+
untracked: { get: () => untracked(readonlyRef) },
|
|
2977
|
+
children: { get: () => children },
|
|
2830
2978
|
});
|
|
2831
|
-
|
|
2832
|
-
return Object.assign(ref, { children });
|
|
2979
|
+
return ref;
|
|
2833
2980
|
});
|
|
2834
2981
|
}
|
|
2835
2982
|
|
|
2836
|
-
class NgtRepeat extends NgForOf {
|
|
2837
|
-
set ngForRepeat(count) {
|
|
2838
|
-
this.ngForOf = Number.isInteger(count) ? Array.from({ length: count }, (_, i) => i) : [];
|
|
2839
|
-
}
|
|
2840
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtRepeat, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2841
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: NgtRepeat, isStandalone: true, selector: "[ngFor][ngForRepeat]", inputs: { ngForRepeat: "ngForRepeat" }, usesInheritance: true, ngImport: i0 }); }
|
|
2842
|
-
}
|
|
2843
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtRepeat, decorators: [{
|
|
2844
|
-
type: Directive,
|
|
2845
|
-
args: [{ selector: '[ngFor][ngForRepeat]', standalone: true }]
|
|
2846
|
-
}], propDecorators: { ngForRepeat: [{
|
|
2847
|
-
type: Input
|
|
2848
|
-
}] } });
|
|
2849
|
-
|
|
2850
2983
|
const privateKeys = [
|
|
2851
2984
|
'get',
|
|
2852
2985
|
'set',
|
|
@@ -2861,38 +2994,38 @@ const privateKeys = [
|
|
|
2861
2994
|
'viewport',
|
|
2862
2995
|
];
|
|
2863
2996
|
class NgtPortalBeforeRender {
|
|
2864
|
-
#portalStore;
|
|
2865
2997
|
constructor() {
|
|
2866
|
-
this
|
|
2998
|
+
this.portalStore = injectNgtStore();
|
|
2999
|
+
this.injector = inject(Injector);
|
|
2867
3000
|
this.renderPriority = 1;
|
|
2868
3001
|
this.beforeRender = new EventEmitter();
|
|
2869
|
-
let oldClear;
|
|
2870
|
-
queueMicrotaskInInjectionContext(() => {
|
|
2871
|
-
injectBeforeRender(({ delta, frame }) => {
|
|
2872
|
-
this.beforeRender.emit({ ...this.#portalStore.get(), delta, frame });
|
|
2873
|
-
const { gl, scene, camera } = this.#portalStore.get();
|
|
2874
|
-
oldClear = gl.autoClear;
|
|
2875
|
-
if (this.renderPriority === 1) {
|
|
2876
|
-
// clear scene and render with default
|
|
2877
|
-
gl.autoClear = true;
|
|
2878
|
-
gl.render(this.parentScene, this.parentCamera);
|
|
2879
|
-
}
|
|
2880
|
-
// disable cleaning
|
|
2881
|
-
gl.autoClear = false;
|
|
2882
|
-
gl.clearDepth();
|
|
2883
|
-
gl.render(scene, camera);
|
|
2884
|
-
// restore
|
|
2885
|
-
gl.autoClear = oldClear;
|
|
2886
|
-
}, { priority: this.renderPriority });
|
|
2887
|
-
});
|
|
2888
3002
|
}
|
|
2889
|
-
|
|
2890
|
-
|
|
3003
|
+
ngOnInit() {
|
|
3004
|
+
let oldClear;
|
|
3005
|
+
injectBeforeRender(({ delta, frame }) => {
|
|
3006
|
+
this.beforeRender.emit({ ...this.portalStore.get(), delta, frame });
|
|
3007
|
+
const { gl, scene, camera } = this.portalStore.get();
|
|
3008
|
+
oldClear = gl.autoClear;
|
|
3009
|
+
if (this.renderPriority === 1) {
|
|
3010
|
+
// clear scene and render with default
|
|
3011
|
+
gl.autoClear = true;
|
|
3012
|
+
gl.render(this.parentScene, this.parentCamera);
|
|
3013
|
+
}
|
|
3014
|
+
// disable cleaning
|
|
3015
|
+
gl.autoClear = false;
|
|
3016
|
+
gl.clearDepth();
|
|
3017
|
+
gl.render(scene, camera);
|
|
3018
|
+
// restore
|
|
3019
|
+
gl.autoClear = oldClear;
|
|
3020
|
+
}, { priority: this.renderPriority, injector: this.injector });
|
|
3021
|
+
}
|
|
3022
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: NgtPortalBeforeRender, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
3023
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.3", type: NgtPortalBeforeRender, isStandalone: true, selector: "[ngtPortalBeforeRender]", inputs: { renderPriority: "renderPriority", parentScene: "parentScene", parentCamera: "parentCamera" }, outputs: { beforeRender: "beforeRender" }, ngImport: i0 }); }
|
|
2891
3024
|
}
|
|
2892
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.
|
|
3025
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: NgtPortalBeforeRender, decorators: [{
|
|
2893
3026
|
type: Directive,
|
|
2894
3027
|
args: [{ selector: '[ngtPortalBeforeRender]', standalone: true }]
|
|
2895
|
-
}],
|
|
3028
|
+
}], propDecorators: { renderPriority: [{
|
|
2896
3029
|
type: Input
|
|
2897
3030
|
}], parentScene: [{
|
|
2898
3031
|
type: Input,
|
|
@@ -2911,96 +3044,90 @@ class NgtPortalContent {
|
|
|
2911
3044
|
delete commentNode[SPECIAL_INTERNAL_ADD_COMMENT];
|
|
2912
3045
|
}
|
|
2913
3046
|
}
|
|
2914
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.
|
|
2915
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.
|
|
3047
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: NgtPortalContent, deps: [{ token: i0.ViewContainerRef }, { token: i0.ViewContainerRef, skipSelf: true }], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
3048
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.3", type: NgtPortalContent, isStandalone: true, selector: "ng-template[ngtPortalContent]", ngImport: i0 }); }
|
|
2916
3049
|
}
|
|
2917
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.
|
|
3050
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: NgtPortalContent, decorators: [{
|
|
2918
3051
|
type: Directive,
|
|
2919
3052
|
args: [{ selector: 'ng-template[ngtPortalContent]', standalone: true }]
|
|
2920
3053
|
}], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.ViewContainerRef, decorators: [{
|
|
2921
3054
|
type: SkipSelf
|
|
2922
3055
|
}] }]; } });
|
|
2923
|
-
class NgtPortal
|
|
3056
|
+
class NgtPortal {
|
|
2924
3057
|
set container(container) {
|
|
2925
|
-
this.set({ container });
|
|
3058
|
+
this.inputs.set({ container });
|
|
2926
3059
|
}
|
|
2927
3060
|
set portalState(state) {
|
|
2928
|
-
this.set({ state });
|
|
2929
|
-
}
|
|
2930
|
-
#parentStore;
|
|
2931
|
-
#portalStore;
|
|
2932
|
-
#injector;
|
|
2933
|
-
#zone;
|
|
2934
|
-
#raycaster;
|
|
2935
|
-
#pointer;
|
|
2936
|
-
#portalContentView;
|
|
3061
|
+
this.inputs.set({ state });
|
|
3062
|
+
}
|
|
2937
3063
|
constructor() {
|
|
2938
|
-
|
|
3064
|
+
this.inputs = signalStore({ container: injectNgtRef(prepare(new THREE.Scene())) });
|
|
2939
3065
|
this.autoRender = true;
|
|
2940
3066
|
this.autoRenderPriority = 1;
|
|
2941
3067
|
this.beforeRender = new EventEmitter();
|
|
2942
|
-
this
|
|
2943
|
-
this.parentScene = this
|
|
2944
|
-
this.parentCamera = this
|
|
2945
|
-
this
|
|
2946
|
-
this
|
|
2947
|
-
this
|
|
2948
|
-
this
|
|
2949
|
-
this
|
|
3068
|
+
this.parentStore = injectNgtStore({ skipSelf: true });
|
|
3069
|
+
this.parentScene = this.parentStore.get('scene');
|
|
3070
|
+
this.parentCamera = this.parentStore.get('camera');
|
|
3071
|
+
this.portalStore = injectNgtStore({ self: true });
|
|
3072
|
+
this.injector = inject(Injector);
|
|
3073
|
+
this.zone = inject(NgZone);
|
|
3074
|
+
this.raycaster = new THREE.Raycaster();
|
|
3075
|
+
this.pointer = new THREE.Vector2();
|
|
2950
3076
|
this.portalContentRendered = false;
|
|
2951
3077
|
inject(DestroyRef).onDestroy(() => {
|
|
2952
|
-
if (this
|
|
2953
|
-
this
|
|
3078
|
+
if (this.portalContentView && !this.portalContentView.destroyed) {
|
|
3079
|
+
this.portalContentView.destroy();
|
|
2954
3080
|
}
|
|
2955
3081
|
});
|
|
2956
3082
|
}
|
|
2957
3083
|
ngOnInit() {
|
|
2958
|
-
const previousState = this
|
|
2959
|
-
const inputsState = this.get();
|
|
3084
|
+
const previousState = this.parentStore.get();
|
|
3085
|
+
const inputsState = this.inputs.get();
|
|
2960
3086
|
if (!inputsState.state && this.autoRender) {
|
|
2961
3087
|
inputsState.state = { events: { priority: this.autoRenderPriority + 1 } };
|
|
2962
3088
|
}
|
|
2963
3089
|
const { events, size, ...restInputsState } = inputsState.state || {};
|
|
2964
3090
|
const containerState = inputsState.container;
|
|
2965
|
-
|
|
3091
|
+
let container = is.ref(containerState) ? containerState.nativeElement : containerState;
|
|
3092
|
+
if (!is.instance(container)) {
|
|
3093
|
+
container = prepare(container);
|
|
3094
|
+
}
|
|
2966
3095
|
const localState = getLocalState(container);
|
|
2967
3096
|
if (!localState.store) {
|
|
2968
|
-
localState.store = this
|
|
3097
|
+
localState.store = this.portalStore;
|
|
2969
3098
|
}
|
|
2970
|
-
this
|
|
3099
|
+
this.portalStore.set({
|
|
2971
3100
|
...previousState,
|
|
2972
3101
|
scene: container,
|
|
2973
|
-
raycaster: this
|
|
2974
|
-
pointer: this
|
|
2975
|
-
|
|
3102
|
+
raycaster: this.raycaster,
|
|
3103
|
+
pointer: this.pointer,
|
|
3104
|
+
previousRoot: this.parentStore,
|
|
2976
3105
|
events: { ...previousState.events, ...(events || {}) },
|
|
2977
3106
|
size: { ...previousState.size, ...(size || {}) },
|
|
2978
3107
|
...restInputsState,
|
|
2979
|
-
|
|
2980
|
-
set: this.#portalStore.set.bind(this.#portalStore),
|
|
2981
|
-
setEvents: (events) => this.#portalStore.set((state) => ({ ...state, events: { ...state.events, ...events } })),
|
|
3108
|
+
setEvents: (events) => this.portalStore.set((state) => ({ ...state, events: { ...state.events, ...events } })),
|
|
2982
3109
|
});
|
|
2983
|
-
const parentState = this
|
|
3110
|
+
const parentState = this.parentStore.select();
|
|
2984
3111
|
effect(() => {
|
|
2985
3112
|
const previous = parentState();
|
|
2986
|
-
this
|
|
2987
|
-
this
|
|
3113
|
+
this.zone.runOutsideAngular(() => {
|
|
3114
|
+
this.portalStore.set((state) => this.inject(previous, state));
|
|
2988
3115
|
});
|
|
2989
|
-
}, { injector: this
|
|
3116
|
+
}, { injector: this.injector });
|
|
2990
3117
|
requestAnimationFrame(() => {
|
|
2991
|
-
this
|
|
3118
|
+
this.portalStore.set((injectState) => this.inject(this.parentStore.get(), injectState));
|
|
2992
3119
|
});
|
|
2993
|
-
this
|
|
2994
|
-
safeDetectChanges(this
|
|
3120
|
+
this.portalContentView = this.portalContentAnchor.createEmbeddedView(this.portalContentTemplate);
|
|
3121
|
+
safeDetectChanges(this.portalContentView);
|
|
2995
3122
|
this.portalContentRendered = true;
|
|
2996
3123
|
}
|
|
2997
3124
|
onBeforeRender(portal) {
|
|
2998
3125
|
this.beforeRender.emit({
|
|
2999
|
-
root: { ...this
|
|
3126
|
+
root: { ...this.parentStore.get(), delta: portal.delta, frame: portal.frame },
|
|
3000
3127
|
portal,
|
|
3001
3128
|
});
|
|
3002
3129
|
}
|
|
3003
|
-
|
|
3130
|
+
inject(rootState, injectState) {
|
|
3004
3131
|
const intersect = { ...rootState };
|
|
3005
3132
|
Object.keys(intersect).forEach((key) => {
|
|
3006
3133
|
if (privateKeys.includes(key) ||
|
|
@@ -3008,7 +3135,7 @@ class NgtPortal extends NgtSignalStore {
|
|
|
3008
3135
|
delete intersect[key];
|
|
3009
3136
|
}
|
|
3010
3137
|
});
|
|
3011
|
-
const inputs = this.get();
|
|
3138
|
+
const inputs = this.inputs.get();
|
|
3012
3139
|
const { size, events, ...restInputsState } = inputs.state || {};
|
|
3013
3140
|
let viewport = undefined;
|
|
3014
3141
|
if (injectState && size) {
|
|
@@ -3020,53 +3147,54 @@ class NgtPortal extends NgtSignalStore {
|
|
|
3020
3147
|
return {
|
|
3021
3148
|
...intersect,
|
|
3022
3149
|
scene: is.ref(inputs.container) ? inputs.container.nativeElement : inputs.container,
|
|
3023
|
-
raycaster: this
|
|
3024
|
-
pointer: this
|
|
3025
|
-
|
|
3150
|
+
raycaster: this.raycaster,
|
|
3151
|
+
pointer: this.pointer,
|
|
3152
|
+
previousRoot: this.parentStore,
|
|
3026
3153
|
events: { ...rootState.events, ...(injectState?.events || {}), ...events },
|
|
3027
3154
|
size: { ...rootState.size, ...size },
|
|
3028
3155
|
viewport: { ...rootState.viewport, ...(viewport || {}) },
|
|
3029
3156
|
...restInputsState,
|
|
3030
3157
|
};
|
|
3031
3158
|
}
|
|
3032
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.
|
|
3033
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3159
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: NgtPortal, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3160
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.3", type: NgtPortal, isStandalone: true, selector: "ngt-portal", inputs: { container: "container", portalState: ["state", "portalState"], autoRender: "autoRender", autoRenderPriority: "autoRenderPriority" }, outputs: { beforeRender: "beforeRender" }, providers: [{ provide: NGT_STORE, useFactory: () => signalStore({}) }], queries: [{ propertyName: "portalContentTemplate", first: true, predicate: NgtPortalContent, descendants: true, read: TemplateRef, static: true }], viewQueries: [{ propertyName: "portalContentAnchor", first: true, predicate: ["portalContentAnchor"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: `
|
|
3161
|
+
<ng-container #portalContentAnchor>
|
|
3162
|
+
<ng-container
|
|
3163
|
+
*ngIf="autoRender && portalContentRendered"
|
|
3164
|
+
ngtPortalBeforeRender
|
|
3165
|
+
[renderPriority]="autoRenderPriority"
|
|
3166
|
+
[parentScene]="parentScene"
|
|
3167
|
+
[parentCamera]="parentCamera"
|
|
3168
|
+
(beforeRender)="onBeforeRender($event)"
|
|
3169
|
+
/>
|
|
3170
|
+
</ng-container>
|
|
3171
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgtPortalBeforeRender, selector: "[ngtPortalBeforeRender]", inputs: ["renderPriority", "parentScene", "parentCamera"], outputs: ["beforeRender"] }] }); }
|
|
3045
3172
|
}
|
|
3046
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.
|
|
3173
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: NgtPortal, decorators: [{
|
|
3047
3174
|
type: Component,
|
|
3048
3175
|
args: [{
|
|
3049
3176
|
selector: 'ngt-portal',
|
|
3050
3177
|
standalone: true,
|
|
3051
3178
|
template: `
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3179
|
+
<ng-container #portalContentAnchor>
|
|
3180
|
+
<ng-container
|
|
3181
|
+
*ngIf="autoRender && portalContentRendered"
|
|
3182
|
+
ngtPortalBeforeRender
|
|
3183
|
+
[renderPriority]="autoRenderPriority"
|
|
3184
|
+
[parentScene]="parentScene"
|
|
3185
|
+
[parentCamera]="parentCamera"
|
|
3186
|
+
(beforeRender)="onBeforeRender($event)"
|
|
3187
|
+
/>
|
|
3188
|
+
</ng-container>
|
|
3189
|
+
`,
|
|
3063
3190
|
imports: [NgIf, NgtPortalBeforeRender],
|
|
3064
|
-
providers: [
|
|
3191
|
+
providers: [{ provide: NGT_STORE, useFactory: () => signalStore({}) }],
|
|
3065
3192
|
}]
|
|
3066
3193
|
}], ctorParameters: function () { return []; }, propDecorators: { container: [{
|
|
3067
3194
|
type: Input
|
|
3068
3195
|
}], portalState: [{
|
|
3069
|
-
type: Input
|
|
3196
|
+
type: Input,
|
|
3197
|
+
args: ['state']
|
|
3070
3198
|
}], autoRender: [{
|
|
3071
3199
|
type: Input
|
|
3072
3200
|
}], autoRenderPriority: [{
|
|
@@ -3090,15 +3218,19 @@ class NgtRoutedScene {
|
|
|
3090
3218
|
.pipe(filter((event) => event instanceof ActivationEnd), takeUntilDestroyed())
|
|
3091
3219
|
.subscribe(() => safeDetectChanges(cdr));
|
|
3092
3220
|
}
|
|
3093
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.
|
|
3094
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.
|
|
3221
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: NgtRoutedScene, deps: [{ token: i1.Router }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3222
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.3", type: NgtRoutedScene, isStandalone: true, selector: "ngt-routed-scene", ngImport: i0, template: `
|
|
3223
|
+
<router-outlet />
|
|
3224
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }] }); }
|
|
3095
3225
|
}
|
|
3096
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.
|
|
3226
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: NgtRoutedScene, decorators: [{
|
|
3097
3227
|
type: Component,
|
|
3098
3228
|
args: [{
|
|
3099
3229
|
standalone: true,
|
|
3100
3230
|
selector: 'ngt-routed-scene',
|
|
3101
|
-
template:
|
|
3231
|
+
template: `
|
|
3232
|
+
<router-outlet />
|
|
3233
|
+
`,
|
|
3102
3234
|
imports: [RouterOutlet],
|
|
3103
3235
|
}]
|
|
3104
3236
|
}], ctorParameters: function () { return [{ type: i1.Router }, { type: i0.ChangeDetectorRef }]; } });
|
|
@@ -3107,5 +3239,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImpor
|
|
|
3107
3239
|
* Generated bundle index. Do not edit.
|
|
3108
3240
|
*/
|
|
3109
3241
|
|
|
3110
|
-
export {
|
|
3242
|
+
export { HTML, NGT_STORE, NgtArgs, NgtCanvas, NgtKey, NgtParent, NgtPortal, NgtPortalContent, NgtRoutedScene, addAfterEffect, addEffect, addTail, applyProps, checkNeedsUpdate, checkUpdate, createAttachFunction, diffProps, extend, getLocalState, injectBeforeRender, injectNgtLoader, injectNgtRef, injectNgtStore, invalidateInstance, is, makeDefaultCamera, makeDefaultRenderer, makeDpr, makeId, makeObjectGraph, prepare, provideNgtRenderer, provideNgtStore, safeDetectChanges, signalStore, updateCamera };
|
|
3111
3243
|
//# sourceMappingURL=angular-three.mjs.map
|