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