angular-three 4.2.3 → 4.2.4-next.0
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/fesm2022/angular-three-dom.mjs +12 -7
- package/fesm2022/angular-three-dom.mjs.map +1 -1
- package/fesm2022/angular-three-testing.mjs +17 -30
- package/fesm2022/angular-three-testing.mjs.map +1 -1
- package/fesm2022/angular-three.mjs +1568 -653
- package/fesm2022/angular-three.mjs.map +1 -1
- package/package.json +1 -1
- package/types/angular-three-dom.d.ts +1 -2
- package/types/angular-three.d.ts +117 -68
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, ViewContainerRef, TemplateRef, Injector, effect, DestroyRef, Directive, input, linkedSignal, computed, InjectionToken, untracked, isSignal, signal, DOCUMENT, Injectable, ElementRef, booleanAttribute, resource, Pipe, numberAttribute, contentChild, SkipSelf, ChangeDetectionStrategy, CUSTOM_ELEMENTS_SCHEMA, Component, output, model
|
|
2
|
+
import { inject, ViewContainerRef, TemplateRef, Renderer2, Injector, effect, DestroyRef, Directive, input, linkedSignal, computed, InjectionToken, untracked, isSignal, signal, DOCUMENT, Injectable, ElementRef, booleanAttribute, resource, Pipe, numberAttribute, contentChild, SkipSelf, ChangeDetectionStrategy, CUSTOM_ELEMENTS_SCHEMA, Component, output, model } from '@angular/core';
|
|
3
3
|
import { Subject, filter } from 'rxjs';
|
|
4
4
|
import * as THREE from 'three';
|
|
5
5
|
import { Group } from 'three';
|
|
@@ -15,29 +15,118 @@ import { ActivationEnd, RouterOutlet } from '@angular/router';
|
|
|
15
15
|
*/
|
|
16
16
|
/** Flag indicating a node is managed by the Angular Three renderer */
|
|
17
17
|
const NGT_RENDERER_NODE_FLAG = '__ngt_renderer__';
|
|
18
|
-
/**
|
|
19
|
-
const
|
|
20
|
-
/** Flag for portal content template comments */
|
|
21
|
-
const NGT_PORTAL_CONTENT_FLAG = '__ngt_renderer_portal_content__';
|
|
22
|
-
/** Flag for args directive comments */
|
|
23
|
-
const NGT_ARGS_FLAG = '__ngt_renderer_args__';
|
|
24
|
-
/** Flag for parent directive comments */
|
|
25
|
-
const NGT_PARENT_FLAG = '__ngt_renderer_parent__';
|
|
26
|
-
/** Internal flag for adding comment nodes */
|
|
27
|
-
const NGT_INTERNAL_ADD_COMMENT_FLAG = '__ngt_renderer_add_comment__';
|
|
28
|
-
/** Internal flag for setting parent on comment nodes */
|
|
29
|
-
const NGT_INTERNAL_SET_PARENT_COMMENT_FLAG = '__ngt_renderer_set_parent_comment__';
|
|
18
|
+
/** Internal hook for scoping structural directive context to embedded view creation */
|
|
19
|
+
const NGT_RENDERER_CONTEXT_FLAG = '__ngt_renderer_context__';
|
|
30
20
|
/** Flag for getting node attributes */
|
|
31
21
|
const NGT_GET_NODE_ATTRIBUTE_FLAG = '__ngt_get_node_attribute__';
|
|
32
22
|
/** Flag for DOM parent element reference */
|
|
33
23
|
const NGT_DOM_PARENT_FLAG = '__ngt_dom_parent__';
|
|
34
|
-
/** Flag indicating the delegate renderer's destroyNode has been patched */
|
|
35
|
-
const NGT_DELEGATE_RENDERER_DESTROY_NODE_PATCHED_FLAG = '__ngt_delegate_renderer_destroy_node_patched__';
|
|
36
24
|
/** Flag for HTML directive classes */
|
|
37
25
|
const NGT_HTML_FLAG = '__ngt_html__';
|
|
38
26
|
/** Native Three.js EventDispatcher events */
|
|
39
27
|
const THREE_NATIVE_EVENTS = ['added', 'removed', 'childadded', 'childremoved', 'change', 'disposed'];
|
|
40
28
|
|
|
29
|
+
function isRendererNode(node) {
|
|
30
|
+
return !!node && typeof node === 'object' && NGT_RENDERER_NODE_FLAG in node;
|
|
31
|
+
}
|
|
32
|
+
function isRendererNodeType(node, type) {
|
|
33
|
+
return isRendererNode(node) && node.__ngt_renderer__[0 /* NgtRendererClassId.type */] === type;
|
|
34
|
+
}
|
|
35
|
+
function createRendererNode(type, node, document) {
|
|
36
|
+
const state = [
|
|
37
|
+
type,
|
|
38
|
+
false,
|
|
39
|
+
undefined,
|
|
40
|
+
undefined,
|
|
41
|
+
undefined,
|
|
42
|
+
[],
|
|
43
|
+
undefined,
|
|
44
|
+
undefined,
|
|
45
|
+
undefined,
|
|
46
|
+
undefined,
|
|
47
|
+
];
|
|
48
|
+
const rendererNode = Object.assign(node, { [NGT_RENDERER_NODE_FLAG]: state });
|
|
49
|
+
// NOTE: assign ownerDocument to node so we can use HostListener in Component
|
|
50
|
+
if (!rendererNode['ownerDocument'])
|
|
51
|
+
rendererNode['ownerDocument'] = document;
|
|
52
|
+
// NOTE: Angular SSR calls `node.getAttribute()` to retrieve hydration info on a node
|
|
53
|
+
if (!('getAttribute' in rendererNode) || typeof rendererNode['getAttribute'] !== 'function') {
|
|
54
|
+
const getNodeAttribute = (name) => rendererNode[name];
|
|
55
|
+
getNodeAttribute[NGT_GET_NODE_ATTRIBUTE_FLAG] = true;
|
|
56
|
+
Object.defineProperty(rendererNode, 'getAttribute', { value: getNodeAttribute, configurable: true });
|
|
57
|
+
}
|
|
58
|
+
return rendererNode;
|
|
59
|
+
}
|
|
60
|
+
function removeRendererChildNode(parent, child) {
|
|
61
|
+
const parentState = parent.__ngt_renderer__;
|
|
62
|
+
const childState = child.__ngt_renderer__;
|
|
63
|
+
const childIndex = parentState[5 /* NgtRendererClassId.children */].indexOf(child);
|
|
64
|
+
if (childIndex >= 0)
|
|
65
|
+
parentState[5 /* NgtRendererClassId.children */].splice(childIndex, 1);
|
|
66
|
+
if (childState[4 /* NgtRendererClassId.parent */] === parent) {
|
|
67
|
+
childState[4 /* NgtRendererClassId.parent */] = undefined;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function insertRendererChildNode(parent, child, refChild) {
|
|
71
|
+
if (parent === child || refChild === child)
|
|
72
|
+
return;
|
|
73
|
+
const childState = child.__ngt_renderer__;
|
|
74
|
+
const previousParent = childState[4 /* NgtRendererClassId.parent */];
|
|
75
|
+
if (previousParent && isRendererNode(previousParent)) {
|
|
76
|
+
removeRendererChildNode(previousParent, child);
|
|
77
|
+
}
|
|
78
|
+
const children = parent.__ngt_renderer__[5 /* NgtRendererClassId.children */];
|
|
79
|
+
const refIndex = refChild ? children.indexOf(refChild) : -1;
|
|
80
|
+
children.splice(refIndex >= 0 ? refIndex : children.length, 0, child);
|
|
81
|
+
childState[4 /* NgtRendererClassId.parent */] = parent;
|
|
82
|
+
assignRendererOwner(parent, child, refChild);
|
|
83
|
+
}
|
|
84
|
+
function getRendererNextSibling(node) {
|
|
85
|
+
const parent = node.__ngt_renderer__[4 /* NgtRendererClassId.parent */];
|
|
86
|
+
if (!parent || !isRendererNode(parent))
|
|
87
|
+
return null;
|
|
88
|
+
const siblings = parent.__ngt_renderer__[5 /* NgtRendererClassId.children */];
|
|
89
|
+
const index = siblings.indexOf(node);
|
|
90
|
+
return index >= 0 ? (siblings[index + 1] ?? null) : null;
|
|
91
|
+
}
|
|
92
|
+
function setRendererAnchor(node, anchor) {
|
|
93
|
+
if (!isRendererNode(node))
|
|
94
|
+
return false;
|
|
95
|
+
node.__ngt_renderer__[7 /* NgtRendererClassId.anchor */] = anchor;
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
function getRendererAnchor(node) {
|
|
99
|
+
return isRendererNode(node) ? node.__ngt_renderer__[7 /* NgtRendererClassId.anchor */] : undefined;
|
|
100
|
+
}
|
|
101
|
+
function markRendererViewHost(node) {
|
|
102
|
+
const state = node.__ngt_renderer__;
|
|
103
|
+
state[9 /* NgtRendererClassId.ownedNodes */] ??= new Set();
|
|
104
|
+
}
|
|
105
|
+
function assignRendererOwner(parent, child, reference) {
|
|
106
|
+
const parentState = parent.__ngt_renderer__;
|
|
107
|
+
const childState = child.__ngt_renderer__;
|
|
108
|
+
if (childState[8 /* NgtRendererClassId.owner */])
|
|
109
|
+
return;
|
|
110
|
+
const parentOwner = parentState[9 /* NgtRendererClassId.ownedNodes */] ? parent : parentState[8 /* NgtRendererClassId.owner */];
|
|
111
|
+
// Canvas and portal anchors are owned by their declaring view but are not logical
|
|
112
|
+
// children of the Three scene returned from parentNode(). Angular still supplies the
|
|
113
|
+
// anchor as the insertion reference, making it the lifecycle bridge into that scene.
|
|
114
|
+
const referenceOwner = isRendererNode(reference) ? reference.__ngt_renderer__[8 /* NgtRendererClassId.owner */] : undefined;
|
|
115
|
+
const owner = parentOwner ?? referenceOwner;
|
|
116
|
+
if (!owner)
|
|
117
|
+
return;
|
|
118
|
+
childState[8 /* NgtRendererClassId.owner */] = owner;
|
|
119
|
+
owner.__ngt_renderer__[9 /* NgtRendererClassId.ownedNodes */]?.add(child);
|
|
120
|
+
}
|
|
121
|
+
function releaseRendererOwner(node, clear = true) {
|
|
122
|
+
const state = node.__ngt_renderer__;
|
|
123
|
+
const owner = state[8 /* NgtRendererClassId.owner */];
|
|
124
|
+
if (owner !== node)
|
|
125
|
+
owner?.__ngt_renderer__[9 /* NgtRendererClassId.ownedNodes */]?.delete(node);
|
|
126
|
+
if (clear)
|
|
127
|
+
state[8 /* NgtRendererClassId.owner */] = undefined;
|
|
128
|
+
}
|
|
129
|
+
|
|
41
130
|
/**
|
|
42
131
|
* Collection of type checking and comparison utilities for Angular Three.
|
|
43
132
|
*
|
|
@@ -151,13 +240,19 @@ class NgtCommonDirective {
|
|
|
151
240
|
constructor() {
|
|
152
241
|
this.vcr = inject(ViewContainerRef);
|
|
153
242
|
this.template = inject(TemplateRef);
|
|
243
|
+
this.renderer = inject(Renderer2);
|
|
154
244
|
this.injector = inject(Injector);
|
|
155
245
|
this.injected = false;
|
|
156
246
|
this.injectedValue = null;
|
|
157
247
|
effect(() => {
|
|
158
|
-
if (this.shouldSkipRender())
|
|
159
|
-
return;
|
|
160
248
|
const value = this.linkedValue();
|
|
249
|
+
if (this.shouldSkipRender()) {
|
|
250
|
+
this.injected = false;
|
|
251
|
+
this.injectedValue = value;
|
|
252
|
+
this.view?.destroy();
|
|
253
|
+
this.view = undefined;
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
161
256
|
if (is.equ(value, this.injectedValue)) {
|
|
162
257
|
// we have the same value as before, no need to update
|
|
163
258
|
return;
|
|
@@ -184,8 +279,14 @@ class NgtCommonDirective {
|
|
|
184
279
|
if (this.view && !this.view.destroyed)
|
|
185
280
|
this.view.destroy();
|
|
186
281
|
this.beforeCreateView();
|
|
187
|
-
|
|
188
|
-
|
|
282
|
+
const createView = () => {
|
|
283
|
+
const view = this.vcr.createEmbeddedView(this.template);
|
|
284
|
+
view.detectChanges();
|
|
285
|
+
return view;
|
|
286
|
+
};
|
|
287
|
+
this.view = this.renderer[NGT_RENDERER_CONTEXT_FLAG]
|
|
288
|
+
? this.renderer[NGT_RENDERER_CONTEXT_FLAG](this.injector, createView)
|
|
289
|
+
: createView();
|
|
189
290
|
}
|
|
190
291
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: NgtCommonDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
191
292
|
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: NgtCommonDirective, isStandalone: true, ngImport: i0 }); }
|
|
@@ -222,12 +323,7 @@ class NgtArgs extends NgtCommonDirective {
|
|
|
222
323
|
return args == null || !Array.isArray(args) || (args.length === 1 && args[0] === null);
|
|
223
324
|
}, ...(ngDevMode ? [{ debugName: "shouldSkipRender" }] : /* istanbul ignore next */ []));
|
|
224
325
|
const commentNode = this.commentNode;
|
|
225
|
-
commentNode.
|
|
226
|
-
commentNode[NGT_ARGS_FLAG] = true;
|
|
227
|
-
if (commentNode[NGT_INTERNAL_ADD_COMMENT_FLAG]) {
|
|
228
|
-
commentNode[NGT_INTERNAL_ADD_COMMENT_FLAG]('args', this.injector);
|
|
229
|
-
delete commentNode[NGT_INTERNAL_ADD_COMMENT_FLAG];
|
|
230
|
-
}
|
|
326
|
+
setRendererAnchor(commentNode, { kind: 'args', injector: this.injector });
|
|
231
327
|
}
|
|
232
328
|
validate() {
|
|
233
329
|
return !this.injected && !!this.injectedValue?.length;
|
|
@@ -299,12 +395,13 @@ function update(timestamp, store, frame) {
|
|
|
299
395
|
let delta = state.clock.getDelta();
|
|
300
396
|
// In frameloop='never' mode, clock times are updated using the provided timestamp
|
|
301
397
|
if (state.frameloop === 'never' && typeof timestamp === 'number') {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
state.clock.
|
|
398
|
+
const elapsedTime = timestamp / 1000;
|
|
399
|
+
delta = elapsedTime - state.clock.elapsedTime;
|
|
400
|
+
state.clock.oldTime = timestamp;
|
|
401
|
+
state.clock.elapsedTime = elapsedTime;
|
|
305
402
|
}
|
|
306
403
|
// Call subscribers (beforeRender)
|
|
307
|
-
const subscribers = state.internal.subscribers;
|
|
404
|
+
const subscribers = state.internal.subscribers.slice();
|
|
308
405
|
for (let i = 0; i < subscribers.length; i++) {
|
|
309
406
|
const subscription = subscribers[i];
|
|
310
407
|
subscription.callback({ ...subscription.store.snapshot, delta, frame });
|
|
@@ -655,6 +752,7 @@ function getInstanceState(obj) {
|
|
|
655
752
|
return undefined;
|
|
656
753
|
return obj.__ngt__ || undefined;
|
|
657
754
|
}
|
|
755
|
+
const pointerEventRegistrations = new WeakMap();
|
|
658
756
|
/**
|
|
659
757
|
* Invalidates an instance, triggering a re-render of the scene.
|
|
660
758
|
*
|
|
@@ -719,8 +817,7 @@ function prepare(object, type, instanceState) {
|
|
|
719
817
|
const [_nonObjects] = [nonObjects(), geometryStamp()];
|
|
720
818
|
return _nonObjects;
|
|
721
819
|
}, ...(ngDevMode ? [{ debugName: "nonObjectsChanged" }] : /* istanbul ignore next */ []));
|
|
722
|
-
instance.__ngt_id__ =
|
|
723
|
-
typeof crypto.randomUUID === 'function' ? crypto.randomUUID() : uuidv4Fallback();
|
|
820
|
+
instance.__ngt_id__ = typeof crypto.randomUUID === 'function' ? crypto.randomUUID() : uuidv4Fallback();
|
|
724
821
|
instance.__ngt__ = {
|
|
725
822
|
previousAttach: null,
|
|
726
823
|
type,
|
|
@@ -731,16 +828,12 @@ function prepare(object, type, instanceState) {
|
|
|
731
828
|
parent: hierarchyStore.parent,
|
|
732
829
|
objects: hierarchyStore.objects,
|
|
733
830
|
nonObjects: nonObjectsChanged,
|
|
734
|
-
add(object, type) {
|
|
831
|
+
add(object, type, before) {
|
|
735
832
|
const current = instance.__ngt__.hierarchyStore.snapshot[type];
|
|
736
|
-
const
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
}
|
|
741
|
-
else {
|
|
742
|
-
instance.__ngt__.hierarchyStore.update((prev) => ({ [type]: [...prev[type], object] }));
|
|
743
|
-
}
|
|
833
|
+
const next = current.filter((node) => object !== node && (!object['uuid'] || !node['uuid'] || object['uuid'] !== node['uuid']));
|
|
834
|
+
const beforeIndex = before ? next.indexOf(before) : -1;
|
|
835
|
+
next.splice(beforeIndex >= 0 ? beforeIndex : next.length, 0, object);
|
|
836
|
+
instance.__ngt__.hierarchyStore.update({ [type]: next });
|
|
744
837
|
notifyAncestors(instance.__ngt__.hierarchyStore.snapshot.parent, type);
|
|
745
838
|
},
|
|
746
839
|
remove(object, type) {
|
|
@@ -763,26 +856,42 @@ function prepare(object, type, instanceState) {
|
|
|
763
856
|
setPointerEvent: {
|
|
764
857
|
value: (eventName, callback) => {
|
|
765
858
|
const iS = getInstanceState(instance);
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
859
|
+
const handlers = (iS.handlers ??= {});
|
|
860
|
+
let registrations = pointerEventRegistrations.get(iS);
|
|
861
|
+
if (!registrations) {
|
|
862
|
+
registrations = new Map();
|
|
863
|
+
pointerEventRegistrations.set(iS, registrations);
|
|
864
|
+
}
|
|
865
|
+
let listeners = registrations.get(eventName);
|
|
866
|
+
if (!listeners) {
|
|
867
|
+
listeners = new Map();
|
|
868
|
+
registrations.set(eventName, listeners);
|
|
869
|
+
const existingHandler = handlers[eventName];
|
|
870
|
+
if (existingHandler)
|
|
871
|
+
listeners.set(Symbol('existing'), existingHandler);
|
|
872
|
+
}
|
|
873
|
+
const token = Symbol(eventName);
|
|
874
|
+
listeners.set(token, callback);
|
|
875
|
+
const dispatch = ((event) => {
|
|
876
|
+
for (const listener of [...listeners.values()])
|
|
877
|
+
listener(event);
|
|
878
|
+
});
|
|
879
|
+
handlers[eventName] = dispatch;
|
|
778
880
|
iS.eventCount += 1;
|
|
779
|
-
|
|
881
|
+
let active = true;
|
|
780
882
|
return () => {
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
883
|
+
if (!active)
|
|
884
|
+
return;
|
|
885
|
+
active = false;
|
|
886
|
+
if (getInstanceState(instance) !== iS || !listeners?.delete(token))
|
|
887
|
+
return;
|
|
888
|
+
iS.eventCount = Math.max(0, iS.eventCount - 1);
|
|
889
|
+
if (listeners.size === 0) {
|
|
890
|
+
registrations?.delete(eventName);
|
|
891
|
+
delete handlers[eventName];
|
|
785
892
|
}
|
|
893
|
+
if (iS.eventCount === 0)
|
|
894
|
+
iS.removeInteraction?.(iS.store);
|
|
786
895
|
};
|
|
787
896
|
},
|
|
788
897
|
configurable: true,
|
|
@@ -818,10 +927,27 @@ function prepare(object, type, instanceState) {
|
|
|
818
927
|
root = root.snapshot.previousRoot;
|
|
819
928
|
}
|
|
820
929
|
if (root.snapshot.internal) {
|
|
821
|
-
const
|
|
822
|
-
const
|
|
930
|
+
const internal = root.snapshot.internal;
|
|
931
|
+
const object = instance;
|
|
932
|
+
const interactions = internal.interaction;
|
|
933
|
+
const index = interactions.findIndex((obj) => obj.uuid === object.uuid);
|
|
823
934
|
if (index >= 0)
|
|
824
935
|
interactions.splice(index, 1);
|
|
936
|
+
internal.initialHits = internal.initialHits.filter((hit) => hit !== object);
|
|
937
|
+
for (const [key, hovered] of internal.hovered) {
|
|
938
|
+
if (hovered.eventObject === object || hovered.object === object)
|
|
939
|
+
internal.hovered.delete(key);
|
|
940
|
+
}
|
|
941
|
+
for (const [pointerId, captures] of internal.capturedMap) {
|
|
942
|
+
const capture = captures.get(object);
|
|
943
|
+
if (!capture)
|
|
944
|
+
continue;
|
|
945
|
+
captures.delete(object);
|
|
946
|
+
if (captures.size === 0) {
|
|
947
|
+
internal.capturedMap.delete(pointerId);
|
|
948
|
+
capture.target.releasePointerCapture(pointerId);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
825
951
|
}
|
|
826
952
|
},
|
|
827
953
|
configurable: true,
|
|
@@ -829,7 +955,8 @@ function prepare(object, type, instanceState) {
|
|
|
829
955
|
});
|
|
830
956
|
return instance;
|
|
831
957
|
}
|
|
832
|
-
const
|
|
958
|
+
const pendingAncestorNotifications = new Map();
|
|
959
|
+
let ancestorNotificationScheduled = false;
|
|
833
960
|
/**
|
|
834
961
|
* Notify ancestors about changes to a THREE.js objects' children
|
|
835
962
|
*
|
|
@@ -837,37 +964,41 @@ const notificationCache = new Map();
|
|
|
837
964
|
* in which case the model matrices will be settled later. `NgtsCenter` needs to know about this
|
|
838
965
|
* matrices change to re-center everything inside of it.
|
|
839
966
|
*
|
|
840
|
-
*
|
|
841
|
-
*
|
|
842
|
-
*
|
|
843
|
-
* 1. If there's no cache or
|
|
844
|
-
* 2. If the type is different for the same instance or
|
|
845
|
-
* 3. We've skipped the notifications for this instance more than a certain amount
|
|
846
|
-
*
|
|
847
|
-
* then we'll proceed with notification
|
|
967
|
+
* Notifications are coalesced by ancestor and hierarchy kind. Angular renderer
|
|
968
|
+
* transactions flush them at `RendererFactory2.end`; direct calls use a microtask fallback.
|
|
848
969
|
*/
|
|
849
970
|
function notifyAncestors(instance, type) {
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
if (notificationCache.size === 1) {
|
|
863
|
-
queueMicrotask(() => notificationCache.clear());
|
|
864
|
-
}
|
|
865
|
-
const { parent } = localState.hierarchyStore.snapshot;
|
|
866
|
-
localState.hierarchyStore.update({ [type]: (localState.hierarchyStore.snapshot[type] || []).slice() });
|
|
867
|
-
notifyAncestors(parent, type);
|
|
971
|
+
let current = instance;
|
|
972
|
+
while (current) {
|
|
973
|
+
const localState = getInstanceState(current);
|
|
974
|
+
if (!localState?.hierarchyStore)
|
|
975
|
+
break;
|
|
976
|
+
let types = pendingAncestorNotifications.get(current);
|
|
977
|
+
if (!types)
|
|
978
|
+
pendingAncestorNotifications.set(current, (types = new Set()));
|
|
979
|
+
types.add(type);
|
|
980
|
+
current = localState.hierarchyStore.snapshot.parent;
|
|
981
|
+
}
|
|
982
|
+
if (pendingAncestorNotifications.size === 0 || ancestorNotificationScheduled)
|
|
868
983
|
return;
|
|
984
|
+
ancestorNotificationScheduled = true;
|
|
985
|
+
queueMicrotask(flushAncestorNotifications);
|
|
986
|
+
}
|
|
987
|
+
/** @internal */
|
|
988
|
+
function flushAncestorNotifications() {
|
|
989
|
+
ancestorNotificationScheduled = false;
|
|
990
|
+
const batch = [...pendingAncestorNotifications];
|
|
991
|
+
pendingAncestorNotifications.clear();
|
|
992
|
+
for (const [ancestor, types] of batch) {
|
|
993
|
+
const localState = getInstanceState(ancestor);
|
|
994
|
+
if (!localState?.hierarchyStore)
|
|
995
|
+
continue;
|
|
996
|
+
const snapshot = localState.hierarchyStore.snapshot;
|
|
997
|
+
const update = {};
|
|
998
|
+
for (const pendingType of types)
|
|
999
|
+
update[pendingType] = snapshot[pendingType].slice();
|
|
1000
|
+
localState.hierarchyStore.update(update);
|
|
869
1001
|
}
|
|
870
|
-
notificationCache.set(id, { ...cached, skipCount: cached.skipCount + 1 });
|
|
871
1002
|
}
|
|
872
1003
|
|
|
873
1004
|
/**
|
|
@@ -890,9 +1021,9 @@ function diffProps(instance, props) {
|
|
|
890
1021
|
key = 'outputColorSpace';
|
|
891
1022
|
}
|
|
892
1023
|
}
|
|
893
|
-
if (is.equ(propValue, instance
|
|
1024
|
+
if (is.equ(propValue, resolveInstanceKey(instance, key).targetProp))
|
|
894
1025
|
continue;
|
|
895
|
-
changes.push([
|
|
1026
|
+
changes.push([key, propValue]);
|
|
896
1027
|
}
|
|
897
1028
|
return changes;
|
|
898
1029
|
}
|
|
@@ -906,6 +1037,41 @@ const NGT_APPLY_PROPS = '__ngt_apply_props__';
|
|
|
906
1037
|
// https://github.com/mrdoob/three.js/pull/27042
|
|
907
1038
|
// https://github.com/mrdoob/three.js/pull/22748
|
|
908
1039
|
const colorMaps = ['map', 'emissiveMap', 'sheenColorMap', 'specularColorMap', 'envMap'];
|
|
1040
|
+
const defaultProperties = new WeakMap();
|
|
1041
|
+
function snapshotPropertyValue(value) {
|
|
1042
|
+
if (value instanceof THREE.Layers) {
|
|
1043
|
+
const snapshot = new THREE.Layers();
|
|
1044
|
+
snapshot.mask = value.mask;
|
|
1045
|
+
return snapshot;
|
|
1046
|
+
}
|
|
1047
|
+
if (ArrayBuffer.isView(value) && 'slice' in value && typeof value.slice === 'function') {
|
|
1048
|
+
return value.slice();
|
|
1049
|
+
}
|
|
1050
|
+
if (value &&
|
|
1051
|
+
typeof value === 'object' &&
|
|
1052
|
+
'clone' in value &&
|
|
1053
|
+
typeof value.clone === 'function' &&
|
|
1054
|
+
'set' in value &&
|
|
1055
|
+
typeof value.set === 'function' &&
|
|
1056
|
+
'copy' in value &&
|
|
1057
|
+
typeof value.copy === 'function') {
|
|
1058
|
+
// Math-like values are mutated in place by applyProps and need a value
|
|
1059
|
+
// snapshot. Disposable resources and other assigned objects deliberately
|
|
1060
|
+
// retain their original identity for default restoration.
|
|
1061
|
+
return value.clone();
|
|
1062
|
+
}
|
|
1063
|
+
return value;
|
|
1064
|
+
}
|
|
1065
|
+
function resolvePropertyValue(root, key, currentValue, nextValue) {
|
|
1066
|
+
let defaults = defaultProperties.get(root);
|
|
1067
|
+
if (!defaults) {
|
|
1068
|
+
defaults = new Map();
|
|
1069
|
+
defaultProperties.set(root, defaults);
|
|
1070
|
+
}
|
|
1071
|
+
if (!defaults.has(key))
|
|
1072
|
+
defaults.set(key, snapshotPropertyValue(currentValue));
|
|
1073
|
+
return nextValue === undefined ? snapshotPropertyValue(defaults.get(key)) : nextValue;
|
|
1074
|
+
}
|
|
909
1075
|
/**
|
|
910
1076
|
* Resolves a property key that may contain dot notation (pierced props).
|
|
911
1077
|
*
|
|
@@ -981,11 +1147,7 @@ function applyProps(instance, props) {
|
|
|
981
1147
|
const rootState = localState?.store?.snapshot ?? instance[NGT_APPLY_PROPS]?.snapshot ?? {};
|
|
982
1148
|
const changes = diffProps(instance, props);
|
|
983
1149
|
for (let i = 0; i < changes.length; i++) {
|
|
984
|
-
|
|
985
|
-
// Ignore setting undefined props
|
|
986
|
-
// https://github.com/pmndrs/react-three-fiber/issues/274
|
|
987
|
-
if (value === undefined)
|
|
988
|
-
continue;
|
|
1150
|
+
const [key, nextValue] = changes[i];
|
|
989
1151
|
// Alias (output)encoding => (output)colorSpace (since r152)
|
|
990
1152
|
// https://github.com/pmndrs/react-three-fiber/pull/2829
|
|
991
1153
|
// if (is.colorSpaceExist(instance)) {
|
|
@@ -1002,10 +1164,7 @@ function applyProps(instance, props) {
|
|
|
1002
1164
|
// }
|
|
1003
1165
|
// }
|
|
1004
1166
|
const { root, targetKey, targetProp } = resolveInstanceKey(instance, key);
|
|
1005
|
-
|
|
1006
|
-
if (root !== instance) {
|
|
1007
|
-
return applyProps(root, { [targetKey]: value });
|
|
1008
|
-
}
|
|
1167
|
+
const value = resolvePropertyValue(root, targetKey, targetProp, nextValue);
|
|
1009
1168
|
// Layers have no copy function, we must therefore copy the mask property
|
|
1010
1169
|
if (targetProp instanceof THREE.Layers && value instanceof THREE.Layers) {
|
|
1011
1170
|
targetProp.mask = value.mask;
|
|
@@ -1064,22 +1223,13 @@ function applyProps(instance, props) {
|
|
|
1064
1223
|
}
|
|
1065
1224
|
checkUpdate(root[targetKey]);
|
|
1066
1225
|
checkUpdate(targetProp);
|
|
1067
|
-
invalidateInstance(instance);
|
|
1068
1226
|
}
|
|
1069
|
-
const instanceHandlersCount = localState?.eventCount;
|
|
1070
1227
|
const parent = localState?.hierarchyStore?.snapshot.parent;
|
|
1071
|
-
if (parent && rootState.internal && instance['raycast'] && instanceHandlersCount !== localState?.eventCount) {
|
|
1072
|
-
// Pre-emptively remove the instance from the interaction manager
|
|
1073
|
-
const index = rootState.internal.interaction.indexOf(instance);
|
|
1074
|
-
if (index > -1)
|
|
1075
|
-
rootState.internal.interaction.splice(index, 1);
|
|
1076
|
-
// Add the instance to the interaction manager only when it has handlers
|
|
1077
|
-
if (localState?.eventCount)
|
|
1078
|
-
rootState.internal.interaction.push(instance);
|
|
1079
|
-
}
|
|
1080
1228
|
if (parent && localState?.onUpdate && changes.length) {
|
|
1081
1229
|
localState.onUpdate(instance);
|
|
1082
1230
|
}
|
|
1231
|
+
if (changes.length)
|
|
1232
|
+
invalidateInstance(instance);
|
|
1083
1233
|
// clearing the intermediate store from the instance
|
|
1084
1234
|
if (instance[NGT_APPLY_PROPS])
|
|
1085
1235
|
delete instance[NGT_APPLY_PROPS];
|
|
@@ -1093,6 +1243,7 @@ function applyProps(instance, props) {
|
|
|
1093
1243
|
* allowing the custom renderer to instantiate objects when elements are created.
|
|
1094
1244
|
*/
|
|
1095
1245
|
const catalogue = {};
|
|
1246
|
+
const catalogueOwnership = new Map();
|
|
1096
1247
|
/**
|
|
1097
1248
|
* Registers Three.js constructors for use in templates.
|
|
1098
1249
|
*
|
|
@@ -1118,10 +1269,59 @@ const catalogue = {};
|
|
|
1118
1269
|
* ```
|
|
1119
1270
|
*/
|
|
1120
1271
|
function extend(objects) {
|
|
1121
|
-
const
|
|
1122
|
-
|
|
1272
|
+
const registrations = Object.entries(objects).map(([key, value]) => {
|
|
1273
|
+
let ownership = catalogueOwnership.get(key);
|
|
1274
|
+
if (!ownership) {
|
|
1275
|
+
ownership = {
|
|
1276
|
+
baseline: catalogue[key],
|
|
1277
|
+
hadBaseline: Object.prototype.hasOwnProperty.call(catalogue, key),
|
|
1278
|
+
registrations: [],
|
|
1279
|
+
};
|
|
1280
|
+
catalogueOwnership.set(key, ownership);
|
|
1281
|
+
}
|
|
1282
|
+
const registrationValue = value;
|
|
1283
|
+
let registration = ownership.registrations.at(-1);
|
|
1284
|
+
if (!registration || registration.value !== registrationValue) {
|
|
1285
|
+
registration = { value: registrationValue, count: 0 };
|
|
1286
|
+
ownership.registrations.push(registration);
|
|
1287
|
+
}
|
|
1288
|
+
// Repeated component-level extend calls commonly register the same Three
|
|
1289
|
+
// constructor. Coalesce those owners so the catalogue retains one layer,
|
|
1290
|
+
// not one record per component instance.
|
|
1291
|
+
registration.count++;
|
|
1292
|
+
catalogue[key] = registration.value;
|
|
1293
|
+
return { key, ownership, registration };
|
|
1294
|
+
});
|
|
1295
|
+
let cleaned = false;
|
|
1123
1296
|
return () => {
|
|
1124
|
-
|
|
1297
|
+
if (cleaned)
|
|
1298
|
+
return;
|
|
1299
|
+
cleaned = true;
|
|
1300
|
+
for (const { key, ownership, registration } of registrations) {
|
|
1301
|
+
// A public remove followed by a new extend starts a new ownership era. An
|
|
1302
|
+
// older cleanup must never affect that newer registration.
|
|
1303
|
+
if (catalogueOwnership.get(key) !== ownership)
|
|
1304
|
+
continue;
|
|
1305
|
+
registration.count--;
|
|
1306
|
+
if (registration.count > 0)
|
|
1307
|
+
continue;
|
|
1308
|
+
const registrationIndex = ownership.registrations.indexOf(registration);
|
|
1309
|
+
if (registrationIndex === -1)
|
|
1310
|
+
continue;
|
|
1311
|
+
ownership.registrations.splice(registrationIndex, 1);
|
|
1312
|
+
const activeRegistration = ownership.registrations.at(-1);
|
|
1313
|
+
if (activeRegistration) {
|
|
1314
|
+
catalogue[key] = activeRegistration.value;
|
|
1315
|
+
continue;
|
|
1316
|
+
}
|
|
1317
|
+
catalogueOwnership.delete(key);
|
|
1318
|
+
if (ownership.hadBaseline) {
|
|
1319
|
+
catalogue[key] = ownership.baseline;
|
|
1320
|
+
}
|
|
1321
|
+
else {
|
|
1322
|
+
delete catalogue[key];
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1125
1325
|
};
|
|
1126
1326
|
}
|
|
1127
1327
|
/**
|
|
@@ -1131,6 +1331,9 @@ function extend(objects) {
|
|
|
1131
1331
|
*/
|
|
1132
1332
|
function remove(...keys) {
|
|
1133
1333
|
for (const key of keys) {
|
|
1334
|
+
// Explicit removal is authoritative. Invalidate all outstanding cleanup
|
|
1335
|
+
// tokens so they cannot restore an older value later.
|
|
1336
|
+
catalogueOwnership.delete(key);
|
|
1134
1337
|
delete catalogue[key];
|
|
1135
1338
|
}
|
|
1136
1339
|
}
|
|
@@ -1147,34 +1350,6 @@ function injectCatalogue() {
|
|
|
1147
1350
|
return inject(NGT_CATALOGUE);
|
|
1148
1351
|
}
|
|
1149
1352
|
|
|
1150
|
-
function isRendererNode(node) {
|
|
1151
|
-
return !!node && typeof node === 'object' && NGT_RENDERER_NODE_FLAG in node;
|
|
1152
|
-
}
|
|
1153
|
-
function createRendererNode(type, node, document) {
|
|
1154
|
-
const state = [type, false, undefined, undefined, undefined, undefined, []];
|
|
1155
|
-
const rendererNode = Object.assign(node, { [NGT_RENDERER_NODE_FLAG]: state });
|
|
1156
|
-
// NOTE: assign ownerDocument to node so we can use HostListener in Component
|
|
1157
|
-
if (!rendererNode['ownerDocument'])
|
|
1158
|
-
rendererNode['ownerDocument'] = document;
|
|
1159
|
-
// NOTE: Angular SSR calls `node.getAttribute()` to retrieve hydration info on a node
|
|
1160
|
-
if (!('getAttribute' in rendererNode) || typeof rendererNode['getAttribute'] !== 'function') {
|
|
1161
|
-
const getNodeAttribute = (name) => rendererNode[name];
|
|
1162
|
-
getNodeAttribute[NGT_GET_NODE_ATTRIBUTE_FLAG] = true;
|
|
1163
|
-
Object.defineProperty(rendererNode, 'getAttribute', { value: getNodeAttribute, configurable: true });
|
|
1164
|
-
}
|
|
1165
|
-
return rendererNode;
|
|
1166
|
-
}
|
|
1167
|
-
function setRendererParentNode(node, parent) {
|
|
1168
|
-
if (!node.__ngt_renderer__[5 /* NgtRendererClassId.parent */]) {
|
|
1169
|
-
node.__ngt_renderer__[5 /* NgtRendererClassId.parent */] = parent;
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
function addRendererChildNode(node, child) {
|
|
1173
|
-
if (!node.__ngt_renderer__[6 /* NgtRendererClassId.children */].includes(child)) {
|
|
1174
|
-
node.__ngt_renderer__[6 /* NgtRendererClassId.children */].push(child);
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
1353
|
const idCache = {};
|
|
1179
1354
|
/**
|
|
1180
1355
|
* Generates a unique identifier.
|
|
@@ -1281,6 +1456,7 @@ function makeObjectGraph(object) {
|
|
|
1281
1456
|
return data;
|
|
1282
1457
|
}
|
|
1283
1458
|
|
|
1459
|
+
const NGT_EVENT_LAYER = Symbol('NGT_EVENT_LAYER');
|
|
1284
1460
|
/**
|
|
1285
1461
|
* @fileoverview Event handling system for Angular Three.
|
|
1286
1462
|
*
|
|
@@ -1374,52 +1550,57 @@ function createEvents(store) {
|
|
|
1374
1550
|
if (!current)
|
|
1375
1551
|
eventsObjects.push(eventsObject);
|
|
1376
1552
|
}
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
const objectRootState = getInstanceState(eventsObjects[i])?.store?.snapshot;
|
|
1388
|
-
if (objectRootState) {
|
|
1389
|
-
objectRootState.raycaster.camera = undefined;
|
|
1390
|
-
}
|
|
1553
|
+
const objectsByLayer = new Map();
|
|
1554
|
+
for (const object of eventsObjects) {
|
|
1555
|
+
const objectStore = getInstanceState(object)?.store;
|
|
1556
|
+
if (!objectStore)
|
|
1557
|
+
continue;
|
|
1558
|
+
const layerObjects = objectsByLayer.get(objectStore);
|
|
1559
|
+
if (layerObjects)
|
|
1560
|
+
layerObjects.push(object);
|
|
1561
|
+
else
|
|
1562
|
+
objectsByLayer.set(objectStore, [object]);
|
|
1391
1563
|
}
|
|
1392
|
-
//
|
|
1564
|
+
// The root computation must precede portal/layer computations, which may derive their
|
|
1565
|
+
// pointer or ray from previousRoot. Each layer is reset, computed, and raycast once.
|
|
1566
|
+
const eventLayers = [];
|
|
1567
|
+
const scheduledLayers = new Set();
|
|
1568
|
+
const scheduleLayer = (eventLayer) => {
|
|
1569
|
+
if (scheduledLayers.has(eventLayer))
|
|
1570
|
+
return;
|
|
1571
|
+
scheduledLayers.add(eventLayer);
|
|
1572
|
+
const previousRoot = eventLayer.snapshot.previousRoot;
|
|
1573
|
+
if (previousRoot)
|
|
1574
|
+
scheduleLayer(previousRoot);
|
|
1575
|
+
eventLayers.push(eventLayer);
|
|
1576
|
+
};
|
|
1577
|
+
scheduleLayer(store);
|
|
1578
|
+
for (const objectStore of objectsByLayer.keys())
|
|
1579
|
+
scheduleLayer(objectStore);
|
|
1580
|
+
for (const eventLayer of eventLayers)
|
|
1581
|
+
eventLayer.snapshot.raycaster.camera = undefined;
|
|
1393
1582
|
const raycastResults = [];
|
|
1394
|
-
|
|
1395
|
-
const
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
// If the camera is still undefined we have to skip this layer entirely
|
|
1404
|
-
if (objState.raycaster.camera === undefined)
|
|
1405
|
-
objState.raycaster.camera = null;
|
|
1406
|
-
}
|
|
1407
|
-
// Intersect object by object
|
|
1408
|
-
return objState.raycaster.camera ? objState.raycaster.intersectObject(obj, true) : [];
|
|
1409
|
-
}
|
|
1410
|
-
// Collect events
|
|
1411
|
-
for (let i = 0; i < eventsObjectsLen; i++) {
|
|
1412
|
-
const objResults = handleRaycast(eventsObjects[i]);
|
|
1413
|
-
if (objResults.length <= 0)
|
|
1583
|
+
for (const eventLayer of eventLayers) {
|
|
1584
|
+
const layerState = eventLayer.snapshot;
|
|
1585
|
+
if (!layerState.events.enabled)
|
|
1586
|
+
continue;
|
|
1587
|
+
layerState.events.compute?.(event, eventLayer, layerState.previousRoot);
|
|
1588
|
+
if (layerState.raycaster.camera === undefined)
|
|
1589
|
+
layerState.raycaster.camera = null;
|
|
1590
|
+
const layerObjects = objectsByLayer.get(eventLayer);
|
|
1591
|
+
if (!layerState.raycaster.camera || !layerObjects?.length)
|
|
1414
1592
|
continue;
|
|
1415
|
-
|
|
1416
|
-
|
|
1593
|
+
const layerResults = layerState.raycaster.intersectObjects(layerObjects, true);
|
|
1594
|
+
for (let index = 0; index < layerResults.length; index++) {
|
|
1595
|
+
const result = layerResults[index];
|
|
1596
|
+
result[NGT_EVENT_LAYER] = eventLayer;
|
|
1597
|
+
raycastResults.push(result);
|
|
1417
1598
|
}
|
|
1418
1599
|
}
|
|
1419
1600
|
// Sort by event priority and distance
|
|
1420
1601
|
raycastResults.sort((a, b) => {
|
|
1421
|
-
const aState = getInstanceState(a.object)?.store?.snapshot;
|
|
1422
|
-
const bState = getInstanceState(b.object)?.store?.snapshot;
|
|
1602
|
+
const aState = a[NGT_EVENT_LAYER]?.snapshot ?? getInstanceState(a.object)?.store?.snapshot;
|
|
1603
|
+
const bState = b[NGT_EVENT_LAYER]?.snapshot ?? getInstanceState(b.object)?.store?.snapshot;
|
|
1423
1604
|
if (!aState || !bState)
|
|
1424
1605
|
return a.distance - b.distance;
|
|
1425
1606
|
return bState.events.priority - aState.events.priority || a.distance - b.distance;
|
|
@@ -1481,7 +1662,8 @@ function createEvents(store) {
|
|
|
1481
1662
|
return;
|
|
1482
1663
|
});
|
|
1483
1664
|
}
|
|
1484
|
-
const
|
|
1665
|
+
const eventLayer = hit[NGT_EVENT_LAYER];
|
|
1666
|
+
const { raycaster, pointer, camera, internal } = eventLayer?.snapshot || instanceState?.store?.snapshot || rootState;
|
|
1485
1667
|
const unprojectedPoint = new THREE.Vector3(pointer.x, pointer.y, 0).unproject(camera);
|
|
1486
1668
|
const hasPointerCapture = (id) => internal.capturedMap.get(id)?.has(hit.eventObject) ?? false;
|
|
1487
1669
|
const setPointerCapture = (id) => {
|
|
@@ -1628,6 +1810,15 @@ function createEvents(store) {
|
|
|
1628
1810
|
const hits = intersect(event, filter);
|
|
1629
1811
|
// Only calculate distance for click events to avoid unnecessary math
|
|
1630
1812
|
const delta = isClickEvent ? calculateDistance(event) : 0;
|
|
1813
|
+
let missedNotified = false;
|
|
1814
|
+
const notifyMissedOnce = () => {
|
|
1815
|
+
if (missedNotified || !isClickEvent)
|
|
1816
|
+
return;
|
|
1817
|
+
missedNotified = true;
|
|
1818
|
+
const missedObjects = internal.interaction.filter((object) => !internal.initialHits.includes(object));
|
|
1819
|
+
if (missedObjects.length > 0)
|
|
1820
|
+
pointerMissed(event, missedObjects);
|
|
1821
|
+
};
|
|
1631
1822
|
// Save initial coordinates on pointer-down
|
|
1632
1823
|
if (name === 'pointerdown') {
|
|
1633
1824
|
internal.initialClick = [event.offsetX, event.offsetY];
|
|
@@ -1685,23 +1876,13 @@ function createEvents(store) {
|
|
|
1685
1876
|
// Forward all events back to their respective handlers with the exception of click events,
|
|
1686
1877
|
// which must use the initial target
|
|
1687
1878
|
if (!isClickEvent || internal.initialHits.includes(eventObject)) {
|
|
1688
|
-
|
|
1689
|
-
const missedObjects = internal.interaction.filter((object) => !internal.initialHits.includes(object));
|
|
1690
|
-
// Call pointerMissed only if we have objects to notify
|
|
1691
|
-
if (missedObjects.length > 0) {
|
|
1692
|
-
pointerMissed(event, missedObjects);
|
|
1693
|
-
}
|
|
1879
|
+
notifyMissedOnce();
|
|
1694
1880
|
// Now call the handler
|
|
1695
1881
|
handler(data);
|
|
1696
1882
|
}
|
|
1697
1883
|
}
|
|
1698
1884
|
else if (isClickEvent && internal.initialHits.includes(eventObject)) {
|
|
1699
|
-
|
|
1700
|
-
const missedObjects = internal.interaction.filter((object) => !internal.initialHits.includes(object));
|
|
1701
|
-
// Call pointerMissed only if we have objects to notify
|
|
1702
|
-
if (missedObjects.length > 0) {
|
|
1703
|
-
pointerMissed(event, missedObjects);
|
|
1704
|
-
}
|
|
1885
|
+
notifyMissedOnce();
|
|
1705
1886
|
}
|
|
1706
1887
|
}
|
|
1707
1888
|
}
|
|
@@ -1760,10 +1941,22 @@ function attach(object, value, paths = [], useApplyProps = false) {
|
|
|
1760
1941
|
function detach(parent, child, attachProp) {
|
|
1761
1942
|
const childInstanceState = getInstanceState(child);
|
|
1762
1943
|
if (childInstanceState) {
|
|
1763
|
-
|
|
1764
|
-
|
|
1944
|
+
const previousAttach = childInstanceState.previousAttach;
|
|
1945
|
+
childInstanceState.previousAttach = undefined;
|
|
1946
|
+
if (Array.isArray(attachProp)) {
|
|
1947
|
+
const attachedValue = attachProp.reduce((value, key) => (value == null ? undefined : value[key]), parent);
|
|
1948
|
+
// Property bindings are applied after Angular has appended every sibling.
|
|
1949
|
+
// An auto-attached resource can therefore retain a stale restoration value
|
|
1950
|
+
// after a later sibling has replaced the same slot. Only the resource that
|
|
1951
|
+
// still owns the slot may restore it. Raw values use applyProps and can be
|
|
1952
|
+
// represented by a transformed value (for example, a THREE.Color), so their
|
|
1953
|
+
// detach path remains unconditional.
|
|
1954
|
+
if (childInstanceState.type !== 'ngt-value' && attachedValue !== child)
|
|
1955
|
+
return;
|
|
1956
|
+
attach(parent, previousAttach, attachProp, childInstanceState.type === 'ngt-value');
|
|
1957
|
+
}
|
|
1765
1958
|
else
|
|
1766
|
-
|
|
1959
|
+
previousAttach?.();
|
|
1767
1960
|
}
|
|
1768
1961
|
}
|
|
1769
1962
|
function assignEmpty(obj, base, shouldAssignStoreForApplyProps = false) {
|
|
@@ -1823,18 +2016,21 @@ function kebabToPascal(str) {
|
|
|
1823
2016
|
}
|
|
1824
2017
|
return pascalStr;
|
|
1825
2018
|
}
|
|
1826
|
-
function propagateStoreRecursively(node, parentNode) {
|
|
2019
|
+
function propagateStoreRecursively(node, parentNode, storeOverride) {
|
|
1827
2020
|
const iS = getInstanceState(node);
|
|
1828
2021
|
const pIS = getInstanceState(parentNode);
|
|
1829
2022
|
if (!iS || !pIS)
|
|
1830
2023
|
return;
|
|
2024
|
+
const store = storeOverride ?? pIS.store;
|
|
2025
|
+
if (!store)
|
|
2026
|
+
return;
|
|
1831
2027
|
// assign store on child if not already exist
|
|
1832
2028
|
// or child store is not the same as parent store
|
|
1833
2029
|
// or child store is the parent of parent store
|
|
1834
|
-
if (!iS.store || iS.store !==
|
|
1835
|
-
iS.store =
|
|
2030
|
+
if (!iS.store || iS.store !== store || iS.store === store.snapshot.previousRoot) {
|
|
2031
|
+
iS.store = store;
|
|
1836
2032
|
// Call addInteraction if it exists
|
|
1837
|
-
iS.addInteraction?.(
|
|
2033
|
+
iS.addInteraction?.(store);
|
|
1838
2034
|
// Collect all children (objects and nonObjects)
|
|
1839
2035
|
const children = [
|
|
1840
2036
|
...(iS.objects ? untracked(iS.objects) : []),
|
|
@@ -1842,60 +2038,88 @@ function propagateStoreRecursively(node, parentNode) {
|
|
|
1842
2038
|
];
|
|
1843
2039
|
// Recursively reassign the store for each child
|
|
1844
2040
|
for (const child of children) {
|
|
1845
|
-
propagateStoreRecursively(child, node);
|
|
2041
|
+
propagateStoreRecursively(child, node, store);
|
|
1846
2042
|
}
|
|
1847
2043
|
}
|
|
1848
2044
|
}
|
|
1849
|
-
function
|
|
2045
|
+
function placeObject3DChild(parent, child, before) {
|
|
2046
|
+
if (child.parent !== parent)
|
|
2047
|
+
parent.add(child);
|
|
2048
|
+
const currentIndex = parent.children.indexOf(child);
|
|
2049
|
+
if (currentIndex < 0)
|
|
2050
|
+
return;
|
|
2051
|
+
parent.children.splice(currentIndex, 1);
|
|
2052
|
+
const beforeIndex = before ? parent.children.indexOf(before) : -1;
|
|
2053
|
+
const insertionIndex = beforeIndex < 0 ? parent.children.length : Math.min(beforeIndex, parent.children.length);
|
|
2054
|
+
parent.children.splice(insertionIndex, 0, child);
|
|
2055
|
+
}
|
|
2056
|
+
function attachThreeNodes(parent, child, before, storeOverride) {
|
|
1850
2057
|
const pIS = getInstanceState(parent);
|
|
1851
2058
|
const cIS = getInstanceState(child);
|
|
1852
2059
|
if (!pIS || !cIS) {
|
|
1853
2060
|
throw new Error(`[NGT] THREE instances need to be prepared with local state.`);
|
|
1854
2061
|
}
|
|
2062
|
+
propagateStoreRecursively(child, parent, storeOverride);
|
|
2063
|
+
if (untracked(cIS.parent) === parent) {
|
|
2064
|
+
if (!cIS.attach &&
|
|
2065
|
+
is.three(parent, 'isObject3D') &&
|
|
2066
|
+
is.three(child, 'isObject3D')) {
|
|
2067
|
+
placeObject3DChild(parent, child, before);
|
|
2068
|
+
pIS.add?.(child, 'objects', before);
|
|
2069
|
+
}
|
|
2070
|
+
else {
|
|
2071
|
+
// Attached resources have no physical sibling order. A logical move must
|
|
2072
|
+
// not invoke their attach callback or overwrite their restoration value.
|
|
2073
|
+
pIS.add?.(child, 'nonObjects', before);
|
|
2074
|
+
}
|
|
2075
|
+
invalidateInstance(parent);
|
|
2076
|
+
return;
|
|
2077
|
+
}
|
|
1855
2078
|
// whether the child is added to the parent with parent.add()
|
|
1856
2079
|
let added = false;
|
|
1857
|
-
// propagate store recursively
|
|
1858
|
-
propagateStoreRecursively(child, parent);
|
|
1859
2080
|
if (cIS.attach) {
|
|
1860
2081
|
const attachProp = cIS.attach;
|
|
1861
2082
|
if (typeof attachProp === 'function') {
|
|
1862
2083
|
let attachCleanUp = undefined;
|
|
1863
2084
|
if (cIS.type === 'ngt-value') {
|
|
1864
|
-
if (cIS.hierarchyStore.snapshot.parent !== parent) {
|
|
1865
|
-
cIS.setParent(parent);
|
|
1866
|
-
}
|
|
1867
2085
|
// at this point we don't have rawValue yet, so we bail and wait until the Renderer recalls attach
|
|
1868
2086
|
if (child.__ngt_renderer__[2 /* NgtRendererClassId.rawValue */] === undefined)
|
|
1869
2087
|
return;
|
|
2088
|
+
if (cIS.hierarchyStore.snapshot.parent !== parent) {
|
|
2089
|
+
cIS.setParent(parent);
|
|
2090
|
+
}
|
|
1870
2091
|
attachCleanUp = attachProp(parent, child.__ngt_renderer__[2 /* NgtRendererClassId.rawValue */], cIS.store);
|
|
1871
2092
|
}
|
|
1872
2093
|
else {
|
|
1873
2094
|
attachCleanUp = attachProp(parent, child, cIS.store);
|
|
1874
2095
|
}
|
|
1875
|
-
|
|
1876
|
-
cIS.previousAttach = attachCleanUp;
|
|
2096
|
+
cIS.previousAttach = attachCleanUp;
|
|
1877
2097
|
}
|
|
1878
2098
|
else {
|
|
1879
2099
|
// we skip attach none if set explicitly
|
|
1880
2100
|
if (attachProp[0] === 'none') {
|
|
2101
|
+
cIS.previousAttach = undefined;
|
|
2102
|
+
pIS.add?.(child, 'nonObjects', before);
|
|
2103
|
+
cIS.setParent(parent);
|
|
1881
2104
|
invalidateInstance(child);
|
|
2105
|
+
invalidateInstance(parent);
|
|
1882
2106
|
return;
|
|
1883
2107
|
}
|
|
1884
2108
|
// handle material array
|
|
1885
2109
|
if (attachProp[0] === 'material' &&
|
|
1886
|
-
attachProp[1] &&
|
|
1887
|
-
|
|
2110
|
+
attachProp[1] !== undefined &&
|
|
2111
|
+
!Number.isNaN(Number(attachProp[1])) &&
|
|
1888
2112
|
is.three(child, 'isMaterial') &&
|
|
1889
2113
|
!Array.isArray(parent['material'])) {
|
|
1890
2114
|
parent['material'] = [];
|
|
1891
2115
|
}
|
|
1892
2116
|
if (cIS.type === 'ngt-value') {
|
|
1893
|
-
if (cIS.hierarchyStore.snapshot.parent !== parent) {
|
|
1894
|
-
cIS.setParent(parent);
|
|
1895
|
-
}
|
|
1896
2117
|
// at this point we don't have rawValue yet, so we bail and wait until the Renderer recalls attach
|
|
1897
2118
|
if (child.__ngt_renderer__[2 /* NgtRendererClassId.rawValue */] === undefined)
|
|
1898
2119
|
return;
|
|
2120
|
+
if (cIS.hierarchyStore.snapshot.parent !== parent) {
|
|
2121
|
+
cIS.setParent(parent);
|
|
2122
|
+
}
|
|
1899
2123
|
// save prev value
|
|
1900
2124
|
cIS.previousAttach = attachProp.reduce((value, key) => value[key], parent);
|
|
1901
2125
|
attach(parent, child.__ngt_renderer__[2 /* NgtRendererClassId.rawValue */], attachProp, true);
|
|
@@ -1908,12 +2132,12 @@ function attachThreeNodes(parent, child) {
|
|
|
1908
2132
|
}
|
|
1909
2133
|
}
|
|
1910
2134
|
else if (is.three(parent, 'isObject3D') && is.three(child, 'isObject3D')) {
|
|
1911
|
-
parent
|
|
2135
|
+
placeObject3DChild(parent, child, before);
|
|
1912
2136
|
added = true;
|
|
1913
2137
|
cIS.addInteraction?.(cIS.store || pIS.store);
|
|
1914
2138
|
}
|
|
1915
2139
|
if (pIS.add) {
|
|
1916
|
-
pIS.add(child, added ? 'objects' : 'nonObjects');
|
|
2140
|
+
pIS.add(child, added ? 'objects' : 'nonObjects', before);
|
|
1917
2141
|
}
|
|
1918
2142
|
if (cIS.parent && untracked(cIS.parent) !== parent) {
|
|
1919
2143
|
cIS.setParent(parent);
|
|
@@ -1925,7 +2149,7 @@ function attachThreeNodes(parent, child) {
|
|
|
1925
2149
|
invalidateInstance(child);
|
|
1926
2150
|
invalidateInstance(parent);
|
|
1927
2151
|
}
|
|
1928
|
-
function removeThreeChild(child, parent, dispose) {
|
|
2152
|
+
function removeThreeChild(child, parent, dispose = false) {
|
|
1929
2153
|
const pIS = getInstanceState(parent);
|
|
1930
2154
|
const cIS = getInstanceState(child);
|
|
1931
2155
|
// clear parent ref
|
|
@@ -1945,8 +2169,9 @@ function removeThreeChild(child, parent, dispose) {
|
|
|
1945
2169
|
}
|
|
1946
2170
|
// dispose
|
|
1947
2171
|
const isPrimitive = cIS?.type && cIS.type === 'ngt-primitive';
|
|
1948
|
-
if (!isPrimitive && child['dispose'] && !is.three(child, 'isScene')) {
|
|
1949
|
-
|
|
2172
|
+
if (dispose && !isPrimitive && child['dispose'] && !is.three(child, 'isScene')) {
|
|
2173
|
+
const disposeInstance = child['dispose'].bind(child);
|
|
2174
|
+
queueMicrotask(disposeInstance);
|
|
1950
2175
|
}
|
|
1951
2176
|
invalidateInstance(parent);
|
|
1952
2177
|
}
|
|
@@ -1954,18 +2179,31 @@ function internalDestroyNode(node, removeChild) {
|
|
|
1954
2179
|
const rS = node.__ngt_renderer__;
|
|
1955
2180
|
if (!rS || rS[1 /* NgtRendererClassId.destroyed */])
|
|
1956
2181
|
return;
|
|
1957
|
-
|
|
2182
|
+
const iS = getInstanceState(node);
|
|
2183
|
+
const physicalParent = iS?.parent ? untracked(iS.parent) : null;
|
|
2184
|
+
if (physicalParent) {
|
|
2185
|
+
removeThreeChild(node, physicalParent, false);
|
|
2186
|
+
}
|
|
2187
|
+
for (const child of rS[5 /* NgtRendererClassId.children */].slice()) {
|
|
2188
|
+
const destructionOwner = rS[9 /* NgtRendererClassId.ownedNodes */] ? node : rS[8 /* NgtRendererClassId.owner */];
|
|
2189
|
+
const childOwner = child.__ngt_renderer__[8 /* NgtRendererClassId.owner */];
|
|
1958
2190
|
removeChild?.(node, child);
|
|
2191
|
+
if (destructionOwner && childOwner && childOwner !== destructionOwner)
|
|
2192
|
+
continue;
|
|
1959
2193
|
internalDestroyNode(child, removeChild);
|
|
1960
2194
|
}
|
|
1961
2195
|
// clear out parent if haven't
|
|
1962
|
-
rS[
|
|
2196
|
+
rS[4 /* NgtRendererClassId.parent */] = undefined;
|
|
1963
2197
|
// clear out children
|
|
1964
|
-
rS[
|
|
2198
|
+
rS[5 /* NgtRendererClassId.children */].length = 0;
|
|
1965
2199
|
// clear out NgtInstanceState
|
|
1966
|
-
const iS = getInstanceState(node);
|
|
1967
2200
|
if (iS) {
|
|
1968
2201
|
const temp = iS;
|
|
2202
|
+
const isPrimitive = iS.type === 'ngt-primitive';
|
|
2203
|
+
if (!isPrimitive && node['dispose'] && !is.three(node, 'isScene')) {
|
|
2204
|
+
const disposeInstance = node['dispose'].bind(node);
|
|
2205
|
+
queueMicrotask(disposeInstance);
|
|
2206
|
+
}
|
|
1969
2207
|
iS.removeInteraction?.(iS.store);
|
|
1970
2208
|
delete temp['onAttach'];
|
|
1971
2209
|
delete temp['onUpdate'];
|
|
@@ -1988,13 +2226,12 @@ function internalDestroyNode(node, removeChild) {
|
|
|
1988
2226
|
delete node['__ngt__'];
|
|
1989
2227
|
}
|
|
1990
2228
|
}
|
|
1991
|
-
// clear
|
|
1992
|
-
rS[
|
|
2229
|
+
// clear renderer metadata
|
|
2230
|
+
rS[6 /* NgtRendererClassId.parentOverride */] = undefined;
|
|
2231
|
+
rS[7 /* NgtRendererClassId.anchor */] = undefined;
|
|
2232
|
+
rS[8 /* NgtRendererClassId.owner */] = undefined;
|
|
2233
|
+
rS[9 /* NgtRendererClassId.ownedNodes */]?.clear();
|
|
1993
2234
|
if (rS[0 /* NgtRendererClassId.type */] === 'comment') {
|
|
1994
|
-
delete node[NGT_INTERNAL_ADD_COMMENT_FLAG];
|
|
1995
|
-
delete node[NGT_INTERNAL_SET_PARENT_COMMENT_FLAG];
|
|
1996
|
-
delete node[NGT_CANVAS_CONTENT_FLAG];
|
|
1997
|
-
delete node[NGT_PORTAL_CONTENT_FLAG];
|
|
1998
2235
|
delete node[NGT_DOM_PARENT_FLAG];
|
|
1999
2236
|
}
|
|
2000
2237
|
// clear getAttribute if exist
|
|
@@ -2007,10 +2244,19 @@ function internalDestroyNode(node, removeChild) {
|
|
|
2007
2244
|
rS[1 /* NgtRendererClassId.destroyed */] = true;
|
|
2008
2245
|
}
|
|
2009
2246
|
|
|
2247
|
+
var _a$1;
|
|
2010
2248
|
/**
|
|
2011
2249
|
* Injection token for renderer factory options.
|
|
2012
2250
|
*/
|
|
2013
2251
|
const NGT_RENDERER_OPTIONS = new InjectionToken('NGT_RENDERER_OPTIONS');
|
|
2252
|
+
/**
|
|
2253
|
+
* Angular's public RendererType2 intentionally omits the component constructor. The runtime
|
|
2254
|
+
* renderer type currently carries it as `type`; keep that private Angular dependency isolated
|
|
2255
|
+
* here so upgrades have one compatibility seam to verify.
|
|
2256
|
+
*/
|
|
2257
|
+
function isNgtHTMLRendererType(type) {
|
|
2258
|
+
return !!Reflect.get(type, 'type')?.[NGT_HTML_FLAG];
|
|
2259
|
+
}
|
|
2014
2260
|
/**
|
|
2015
2261
|
* Angular renderer factory for Three.js elements.
|
|
2016
2262
|
*
|
|
@@ -2039,46 +2285,45 @@ class NgtRendererFactory2 {
|
|
|
2039
2285
|
this.catalogue = injectCatalogue();
|
|
2040
2286
|
this.document = inject(DOCUMENT);
|
|
2041
2287
|
this.options = inject(NGT_RENDERER_OPTIONS, { optional: true }) || {};
|
|
2042
|
-
this.
|
|
2288
|
+
this.rendererByDelegate = new WeakMap();
|
|
2043
2289
|
}
|
|
2044
2290
|
createRenderer(hostElement, type) {
|
|
2045
2291
|
const delegateRenderer = this.delegateRendererFactory.createRenderer(hostElement, type);
|
|
2046
2292
|
if (!type)
|
|
2047
2293
|
return delegateRenderer;
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2294
|
+
if (isNgtHTMLRendererType(type))
|
|
2295
|
+
return delegateRenderer;
|
|
2296
|
+
if (hostElement) {
|
|
2297
|
+
let hostRendererNode;
|
|
2298
|
+
if (isRendererNode(hostElement)) {
|
|
2299
|
+
hostRendererNode = hostElement;
|
|
2300
|
+
}
|
|
2301
|
+
else {
|
|
2302
|
+
const logicalParent = delegateRenderer.parentNode(hostElement);
|
|
2303
|
+
hostRendererNode = createRendererNode('platform', hostElement, this.document);
|
|
2304
|
+
if (isRendererNode(logicalParent)) {
|
|
2305
|
+
insertRendererChildNode(logicalParent, hostRendererNode);
|
|
2054
2306
|
}
|
|
2055
2307
|
}
|
|
2056
|
-
|
|
2308
|
+
markRendererViewHost(hostRendererNode);
|
|
2057
2309
|
}
|
|
2058
|
-
|
|
2059
|
-
|
|
2310
|
+
let renderer = this.rendererByDelegate.get(delegateRenderer);
|
|
2311
|
+
if (!renderer) {
|
|
2312
|
+
renderer = new NgtRenderer2(delegateRenderer, this.catalogue, this.document, this.options);
|
|
2313
|
+
this.rendererByDelegate.set(delegateRenderer, renderer);
|
|
2060
2314
|
}
|
|
2061
|
-
if (Reflect.get(type, 'type')?.[NGT_HTML_FLAG]) {
|
|
2062
|
-
this.rendererMap.set(type.id, delegateRenderer);
|
|
2063
|
-
// patch delegate destroyNode so we can destroy this HTML node
|
|
2064
|
-
// TODO: make sure we really need to do this
|
|
2065
|
-
const originalDestroyNode = delegateRenderer.destroyNode?.bind(delegateRenderer);
|
|
2066
|
-
if (!originalDestroyNode || !(NGT_DELEGATE_RENDERER_DESTROY_NODE_PATCHED_FLAG in originalDestroyNode)) {
|
|
2067
|
-
delegateRenderer.destroyNode = (node) => {
|
|
2068
|
-
originalDestroyNode?.(node);
|
|
2069
|
-
if (node !== hostElement)
|
|
2070
|
-
return;
|
|
2071
|
-
internalDestroyNode(node, null);
|
|
2072
|
-
};
|
|
2073
|
-
Object.assign(delegateRenderer.destroyNode, {
|
|
2074
|
-
[NGT_DELEGATE_RENDERER_DESTROY_NODE_PATCHED_FLAG]: true,
|
|
2075
|
-
});
|
|
2076
|
-
}
|
|
2077
|
-
return delegateRenderer;
|
|
2078
|
-
}
|
|
2079
|
-
this.rendererMap.set(type.id, (renderer = new NgtRenderer2(delegateRenderer, this.catalogue, this.document, this.options)));
|
|
2080
2315
|
return renderer;
|
|
2081
2316
|
}
|
|
2317
|
+
begin() {
|
|
2318
|
+
this.delegateRendererFactory.begin?.();
|
|
2319
|
+
}
|
|
2320
|
+
end() {
|
|
2321
|
+
flushAncestorNotifications();
|
|
2322
|
+
this.delegateRendererFactory.end?.();
|
|
2323
|
+
}
|
|
2324
|
+
whenRenderingDone() {
|
|
2325
|
+
return this.delegateRendererFactory.whenRenderingDone?.() ?? Promise.resolve();
|
|
2326
|
+
}
|
|
2082
2327
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: NgtRendererFactory2, deps: [{ token: i0.RendererFactory2 }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2083
2328
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: NgtRendererFactory2 }); }
|
|
2084
2329
|
}
|
|
@@ -2098,40 +2343,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
2098
2343
|
* @internal
|
|
2099
2344
|
*/
|
|
2100
2345
|
class NgtRenderer2 {
|
|
2101
|
-
|
|
2346
|
+
static { _a$1 = NGT_RENDERER_CONTEXT_FLAG; }
|
|
2347
|
+
constructor(delegateRenderer, catalogue, document, options) {
|
|
2102
2348
|
this.delegateRenderer = delegateRenderer;
|
|
2103
2349
|
this.catalogue = catalogue;
|
|
2104
2350
|
this.document = document;
|
|
2105
2351
|
this.options = options;
|
|
2106
|
-
this.
|
|
2107
|
-
this.
|
|
2108
|
-
this.
|
|
2109
|
-
this.
|
|
2110
|
-
|
|
2352
|
+
this.directiveInjectors = [];
|
|
2353
|
+
this.parameterKeys = new WeakMap();
|
|
2354
|
+
this.attachedListeners = new WeakMap();
|
|
2355
|
+
this.updatedListeners = new WeakMap();
|
|
2356
|
+
this[_a$1] = (injector, callback) => {
|
|
2357
|
+
this.directiveInjectors.push(injector);
|
|
2358
|
+
try {
|
|
2359
|
+
return callback();
|
|
2360
|
+
}
|
|
2361
|
+
finally {
|
|
2362
|
+
this.directiveInjectors.pop();
|
|
2363
|
+
}
|
|
2111
2364
|
};
|
|
2112
|
-
this.
|
|
2113
|
-
this.removeClass = this.delegateRenderer.removeClass.bind(this.delegateRenderer);
|
|
2114
|
-
this.setStyle = this.delegateRenderer.setStyle.bind(this.delegateRenderer);
|
|
2115
|
-
this.removeStyle = this.delegateRenderer.removeStyle.bind(this.delegateRenderer);
|
|
2116
|
-
this.selectRootElement = this.delegateRenderer.selectRootElement.bind(this.delegateRenderer);
|
|
2117
|
-
this.nextSibling = this.delegateRenderer.nextSibling.bind(this.delegateRenderer);
|
|
2118
|
-
this.setValue = this.delegateRenderer.setValue.bind(this.delegateRenderer);
|
|
2365
|
+
this.data = { ...this.delegateRenderer.data, __ngt_renderer__: true };
|
|
2119
2366
|
if (!this.options.verbose) {
|
|
2120
2367
|
this.options.verbose = false;
|
|
2121
2368
|
}
|
|
2122
2369
|
}
|
|
2123
|
-
get data() {
|
|
2124
|
-
return { ...this.delegateRenderer.data, __ngt_renderer__: true };
|
|
2125
|
-
}
|
|
2126
2370
|
destroy() {
|
|
2127
|
-
|
|
2128
|
-
this.count -= 1;
|
|
2129
|
-
return;
|
|
2130
|
-
}
|
|
2131
|
-
// this is the last instance of the same NgtRenderer2
|
|
2132
|
-
this.count = 0;
|
|
2133
|
-
this.argsInjectors = [];
|
|
2134
|
-
this.parentInjectors = [];
|
|
2371
|
+
this.delegateRenderer.destroy();
|
|
2135
2372
|
}
|
|
2136
2373
|
createElement(name, namespace) {
|
|
2137
2374
|
const platformElement = this.delegateRenderer.createElement(name, namespace);
|
|
@@ -2142,8 +2379,8 @@ class NgtRenderer2 {
|
|
|
2142
2379
|
return createRendererNode('three', prepare(platformElement, 'ngt-value'), this.document);
|
|
2143
2380
|
}
|
|
2144
2381
|
const [injectedArgs, injectedParent] = [
|
|
2145
|
-
this.getNgtDirective(NgtArgs
|
|
2146
|
-
this.getNgtDirective(NgtParent
|
|
2382
|
+
this.getNgtDirective(NgtArgs)?.value || [],
|
|
2383
|
+
this.getNgtDirective(NgtParent)?.value,
|
|
2147
2384
|
];
|
|
2148
2385
|
if (name === 'ngt-primitive') {
|
|
2149
2386
|
if (!injectedArgs[0])
|
|
@@ -2155,7 +2392,7 @@ class NgtRenderer2 {
|
|
|
2155
2392
|
prepare(object, 'ngt-primitive');
|
|
2156
2393
|
const primitiveRendererNode = createRendererNode('three', object, this.document);
|
|
2157
2394
|
if (injectedParent) {
|
|
2158
|
-
primitiveRendererNode.__ngt_renderer__[
|
|
2395
|
+
primitiveRendererNode.__ngt_renderer__[6 /* NgtRendererClassId.parentOverride */] =
|
|
2159
2396
|
injectedParent;
|
|
2160
2397
|
}
|
|
2161
2398
|
return primitiveRendererNode;
|
|
@@ -2185,7 +2422,7 @@ class NgtRenderer2 {
|
|
|
2185
2422
|
instanceState.attach = ['material'];
|
|
2186
2423
|
}
|
|
2187
2424
|
if (injectedParent) {
|
|
2188
|
-
rendererNode.__ngt_renderer__[
|
|
2425
|
+
rendererNode.__ngt_renderer__[6 /* NgtRendererClassId.parentOverride */] =
|
|
2189
2426
|
injectedParent;
|
|
2190
2427
|
}
|
|
2191
2428
|
return rendererNode;
|
|
@@ -2195,31 +2432,24 @@ class NgtRenderer2 {
|
|
|
2195
2432
|
createComment(value) {
|
|
2196
2433
|
const commentNode = this.delegateRenderer.createComment(value);
|
|
2197
2434
|
const commentRendererNode = createRendererNode('comment', commentNode, this.document);
|
|
2198
|
-
// NOTE: we attach an arrow function to the Comment node
|
|
2199
|
-
// In our directives, we can call this function to then start tracking the RendererNode
|
|
2200
|
-
// this is done to limit the amount of Nodes we need to process for getCreationState
|
|
2201
|
-
Object.assign(commentRendererNode, {
|
|
2202
|
-
[NGT_INTERNAL_ADD_COMMENT_FLAG]: (type, injector) => {
|
|
2203
|
-
if (type === 'args') {
|
|
2204
|
-
this.argsInjectors.push(injector);
|
|
2205
|
-
}
|
|
2206
|
-
else if (type === 'parent') {
|
|
2207
|
-
Object.assign(commentRendererNode, {
|
|
2208
|
-
[NGT_INTERNAL_SET_PARENT_COMMENT_FLAG]: (ngtParent) => {
|
|
2209
|
-
commentRendererNode.__ngt_renderer__[5 /* NgtRendererClassId.parent */] = ngtParent;
|
|
2210
|
-
},
|
|
2211
|
-
});
|
|
2212
|
-
this.parentInjectors.push(injector);
|
|
2213
|
-
}
|
|
2214
|
-
commentRendererNode.__ngt_renderer__[4 /* NgtRendererClassId.injector */] = injector;
|
|
2215
|
-
},
|
|
2216
|
-
});
|
|
2217
2435
|
return commentRendererNode;
|
|
2218
2436
|
}
|
|
2219
2437
|
createText(value) {
|
|
2220
2438
|
const textNode = this.delegateRenderer.createText(value);
|
|
2221
2439
|
return createRendererNode('text', textNode, this.document);
|
|
2222
2440
|
}
|
|
2441
|
+
destroyNode(node) {
|
|
2442
|
+
const shouldDelegate = !isRendererNode(node) || node.__ngt_renderer__[0 /* NgtRendererClassId.type */] !== 'three';
|
|
2443
|
+
if (isRendererNode(node)) {
|
|
2444
|
+
this.destroyOwnedNodes(node);
|
|
2445
|
+
// Unlink from the owner's set but retain the owner marker until recursive
|
|
2446
|
+
// destruction has used it to protect foreign/projected descendants.
|
|
2447
|
+
releaseRendererOwner(node, false);
|
|
2448
|
+
}
|
|
2449
|
+
internalDestroyNode(node, this.removeChild.bind(this));
|
|
2450
|
+
if (shouldDelegate)
|
|
2451
|
+
this.delegateRenderer.destroyNode?.(node);
|
|
2452
|
+
}
|
|
2223
2453
|
appendChild(parent, newChild, refChild, isMove) {
|
|
2224
2454
|
const delegatedFn = refChild
|
|
2225
2455
|
? this.delegateRenderer.insertBefore.bind(this.delegateRenderer, parent, newChild, refChild, isMove)
|
|
@@ -2231,10 +2461,13 @@ class NgtRenderer2 {
|
|
|
2231
2461
|
console.warn('[NGT dev mode] One of parent or child is not a renderer node.', { parent, newChild });
|
|
2232
2462
|
return delegatedFn();
|
|
2233
2463
|
}
|
|
2464
|
+
if (parent === newChild || refChild === newChild)
|
|
2465
|
+
return;
|
|
2466
|
+
this.setNodeRelationship(parent, newChild, refChild);
|
|
2234
2467
|
if (cRS[0 /* NgtRendererClassId.type */] === 'comment') {
|
|
2235
|
-
//
|
|
2468
|
+
// A comment is a logical anchor. It remains in the ordered host tree even when
|
|
2469
|
+
// the physical Three graph has no corresponding node.
|
|
2236
2470
|
// comment usually means it's part of a templateRef ViewContainerRef or structural directive
|
|
2237
|
-
setRendererParentNode(newChild, parent);
|
|
2238
2471
|
// if parent is not three, we'll delegate to the renderer
|
|
2239
2472
|
if (pRS[0 /* NgtRendererClassId.type */] !== 'three') {
|
|
2240
2473
|
delegatedFn();
|
|
@@ -2242,52 +2475,43 @@ class NgtRenderer2 {
|
|
|
2242
2475
|
return;
|
|
2243
2476
|
}
|
|
2244
2477
|
if (pRS[0 /* NgtRendererClassId.type */] === 'platform' && cRS[0 /* NgtRendererClassId.type */] === 'platform') {
|
|
2245
|
-
|
|
2246
|
-
|
|
2478
|
+
const threeParent = this.findNearestThreeParent(parent);
|
|
2479
|
+
const portalStore = this.findNearestPortalStore(parent);
|
|
2480
|
+
const insertionReference = threeParent
|
|
2481
|
+
? this.findThreeReferenceAfterLogicalNode(threeParent, newChild)
|
|
2482
|
+
: null;
|
|
2483
|
+
if (newChild[NGT_DOM_PARENT_FLAG]) {
|
|
2484
|
+
this.delegateRenderer.appendChild(newChild[NGT_DOM_PARENT_FLAG], newChild);
|
|
2247
2485
|
}
|
|
2248
|
-
|
|
2249
|
-
|
|
2486
|
+
else {
|
|
2487
|
+
delegatedFn();
|
|
2250
2488
|
}
|
|
2251
|
-
|
|
2489
|
+
if (threeParent)
|
|
2490
|
+
this.attachThreeDescendants(threeParent, newChild, insertionReference, portalStore);
|
|
2491
|
+
return;
|
|
2252
2492
|
}
|
|
2253
|
-
if (
|
|
2254
|
-
return this.appendThreeRendererNodes(parent, newChild);
|
|
2493
|
+
if (isRendererNodeType(parent, 'three') && isRendererNodeType(newChild, 'three')) {
|
|
2494
|
+
return this.appendThreeRendererNodes(parent, newChild, refChild);
|
|
2255
2495
|
}
|
|
2256
|
-
if (pRS[0 /* NgtRendererClassId.type */] === 'platform' &&
|
|
2257
|
-
|
|
2258
|
-
if (
|
|
2259
|
-
|
|
2260
|
-
addRendererChildNode(parent, newChild);
|
|
2261
|
-
return this.appendChild(pRS[5 /* NgtRendererClassId.parent */], newChild);
|
|
2496
|
+
if (pRS[0 /* NgtRendererClassId.type */] === 'platform' && isRendererNodeType(newChild, 'three')) {
|
|
2497
|
+
const threeParent = this.findNearestThreeParent(parent);
|
|
2498
|
+
if (threeParent) {
|
|
2499
|
+
this.appendThreeRendererNodes(threeParent, newChild, refChild, this.findNearestPortalStore(parent));
|
|
2262
2500
|
}
|
|
2263
|
-
// platform can also have normal parentNode
|
|
2264
|
-
const platformParentNode = this.delegateRenderer.parentNode(parent);
|
|
2265
|
-
if (platformParentNode) {
|
|
2266
|
-
return this.appendChild(platformParentNode, newChild);
|
|
2267
|
-
}
|
|
2268
|
-
// if not, set up parent and child relationship for this pair then bail
|
|
2269
|
-
this.setNodeRelationship(parent, newChild);
|
|
2270
2501
|
return;
|
|
2271
2502
|
}
|
|
2272
|
-
if (
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
this.
|
|
2278
|
-
}
|
|
2279
|
-
for (const platformChildNode of newChild['childNodes'] || []) {
|
|
2280
|
-
if (!isRendererNode(platformChildNode) ||
|
|
2281
|
-
platformChildNode.__ngt_renderer__[0 /* NgtRendererClassId.type */] !== 'platform')
|
|
2282
|
-
continue;
|
|
2283
|
-
this.appendChild(parent, platformChildNode);
|
|
2284
|
-
}
|
|
2503
|
+
if (isRendererNodeType(parent, 'three') &&
|
|
2504
|
+
(cRS[0 /* NgtRendererClassId.type */] === 'platform' ||
|
|
2505
|
+
cRS[0 /* NgtRendererClassId.type */] === 'portal' ||
|
|
2506
|
+
cRS[0 /* NgtRendererClassId.type */] === 'text')) {
|
|
2507
|
+
if (isRendererNodeType(newChild, 'platform'))
|
|
2508
|
+
this.attachThreeDescendants(parent, newChild);
|
|
2285
2509
|
return;
|
|
2286
2510
|
}
|
|
2287
|
-
if (
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2511
|
+
if (isRendererNodeType(parent, 'portal') && isRendererNodeType(newChild, 'three')) {
|
|
2512
|
+
const portalContainer = parent.__ngt_renderer__[3 /* NgtRendererClassId.portalContainer */];
|
|
2513
|
+
if (portalContainer)
|
|
2514
|
+
return this.appendThreeRendererNodes(portalContainer, newChild, refChild, this.portalStore(parent));
|
|
2291
2515
|
return;
|
|
2292
2516
|
}
|
|
2293
2517
|
if (pRS[0 /* NgtRendererClassId.type */] === 'platform' && cRS[0 /* NgtRendererClassId.type */] === 'portal') {
|
|
@@ -2296,14 +2520,12 @@ class NgtRenderer2 {
|
|
|
2296
2520
|
return delegatedFn();
|
|
2297
2521
|
}
|
|
2298
2522
|
insertBefore(parent, newChild, refChild, isMove) {
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
newChild instanceof Comment) {
|
|
2306
|
-
Object.assign(newChild, { [NGT_CANVAS_CONTENT_FLAG]: refChild[NGT_CANVAS_CONTENT_FLAG] });
|
|
2523
|
+
const referenceAnchor = getRendererAnchor(refChild);
|
|
2524
|
+
if (referenceAnchor &&
|
|
2525
|
+
(referenceAnchor.kind === 'canvas' || referenceAnchor.kind === 'portal') &&
|
|
2526
|
+
isRendererNode(newChild) &&
|
|
2527
|
+
newChild.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'comment') {
|
|
2528
|
+
setRendererAnchor(newChild, referenceAnchor);
|
|
2307
2529
|
}
|
|
2308
2530
|
// if there is no parent, we delegate
|
|
2309
2531
|
if (!parent) {
|
|
@@ -2311,21 +2533,19 @@ class NgtRenderer2 {
|
|
|
2311
2533
|
}
|
|
2312
2534
|
return this.appendChild(parent, newChild, refChild, isMove);
|
|
2313
2535
|
}
|
|
2314
|
-
removeChild(parent, oldChild, isHostElement) {
|
|
2536
|
+
removeChild(parent, oldChild, isHostElement, requireSynchronousElementRemoval) {
|
|
2315
2537
|
if (parent === null) {
|
|
2316
2538
|
parent = this.parentNode(oldChild);
|
|
2317
2539
|
}
|
|
2318
2540
|
const cRS = oldChild.__ngt_renderer__;
|
|
2319
2541
|
if (!cRS) {
|
|
2320
2542
|
try {
|
|
2321
|
-
return this.delegateRenderer.removeChild(parent, oldChild, isHostElement);
|
|
2543
|
+
return this.delegateRenderer.removeChild(parent, oldChild, isHostElement, requireSynchronousElementRemoval);
|
|
2322
2544
|
}
|
|
2323
2545
|
catch {
|
|
2324
2546
|
return;
|
|
2325
2547
|
}
|
|
2326
2548
|
}
|
|
2327
|
-
// disassociate things from oldChild
|
|
2328
|
-
cRS[5 /* NgtRendererClassId.parent */] = null;
|
|
2329
2549
|
// if parent is still undefined
|
|
2330
2550
|
if (parent == null) {
|
|
2331
2551
|
if (cRS[1 /* NgtRendererClassId.destroyed */]) {
|
|
@@ -2338,39 +2558,38 @@ class NgtRenderer2 {
|
|
|
2338
2558
|
}
|
|
2339
2559
|
const pRS = parent.__ngt_renderer__;
|
|
2340
2560
|
if (!pRS) {
|
|
2341
|
-
return this.delegateRenderer.removeChild(parent, oldChild, isHostElement);
|
|
2561
|
+
return this.delegateRenderer.removeChild(parent, oldChild, isHostElement, requireSynchronousElementRemoval);
|
|
2342
2562
|
}
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
// disassociate oldChild from parent children
|
|
2346
|
-
pRS[6 /* NgtRendererClassId.children */].splice(childIndex, 1);
|
|
2563
|
+
if (cRS[0 /* NgtRendererClassId.type */] !== 'three') {
|
|
2564
|
+
this.detachThreeDescendants(oldChild);
|
|
2347
2565
|
}
|
|
2348
|
-
|
|
2349
|
-
|
|
2566
|
+
removeRendererChildNode(parent, oldChild);
|
|
2567
|
+
if (cRS[0 /* NgtRendererClassId.type */] === 'three') {
|
|
2568
|
+
const childState = getInstanceState(oldChild);
|
|
2569
|
+
const threeParent = childState?.parent ? untracked(childState.parent) : null;
|
|
2570
|
+
if (threeParent) {
|
|
2571
|
+
return removeThreeChild(oldChild, threeParent, false);
|
|
2572
|
+
}
|
|
2573
|
+
return;
|
|
2350
2574
|
}
|
|
2351
2575
|
if (pRS[0 /* NgtRendererClassId.type */] === 'platform' && cRS[0 /* NgtRendererClassId.type */] === 'platform') {
|
|
2352
|
-
return this.delegateRenderer.removeChild(parent, oldChild, isHostElement);
|
|
2576
|
+
return this.delegateRenderer.removeChild(parent, oldChild, isHostElement, requireSynchronousElementRemoval);
|
|
2353
2577
|
}
|
|
2354
|
-
if (pRS[0 /* NgtRendererClassId.type */] === 'three' &&
|
|
2578
|
+
if (pRS[0 /* NgtRendererClassId.type */] === 'three' &&
|
|
2579
|
+
(cRS[0 /* NgtRendererClassId.type */] === 'platform' ||
|
|
2580
|
+
cRS[0 /* NgtRendererClassId.type */] === 'portal' ||
|
|
2581
|
+
cRS[0 /* NgtRendererClassId.type */] === 'text')) {
|
|
2355
2582
|
return;
|
|
2356
2583
|
}
|
|
2357
|
-
|
|
2358
|
-
const childLS = getInstanceState(oldChild);
|
|
2359
|
-
if (!childLS)
|
|
2360
|
-
return;
|
|
2361
|
-
const threeParent = childLS.parent ? untracked(childLS.parent) : null;
|
|
2362
|
-
if (!threeParent)
|
|
2363
|
-
return;
|
|
2364
|
-
return this.removeChild(threeParent, oldChild);
|
|
2365
|
-
}
|
|
2366
|
-
return this.delegateRenderer.removeChild(parent, oldChild, isHostElement);
|
|
2584
|
+
return this.delegateRenderer.removeChild(parent, oldChild, isHostElement, requireSynchronousElementRemoval);
|
|
2367
2585
|
}
|
|
2368
2586
|
parentNode(node) {
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
isRendererNode(node)
|
|
2373
|
-
|
|
2587
|
+
const anchor = getRendererAnchor(node);
|
|
2588
|
+
if (anchor &&
|
|
2589
|
+
(anchor.kind === 'canvas' || anchor.kind === 'portal') &&
|
|
2590
|
+
isRendererNode(node) &&
|
|
2591
|
+
node.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'comment') {
|
|
2592
|
+
const store = anchor.store;
|
|
2374
2593
|
// this should not happen but if it does, we'll delegate to the renderer
|
|
2375
2594
|
if (!store) {
|
|
2376
2595
|
return this.delegateRenderer.parentNode(node);
|
|
@@ -2380,24 +2599,18 @@ class NgtRenderer2 {
|
|
|
2380
2599
|
if (!rootScene) {
|
|
2381
2600
|
return this.delegateRenderer.parentNode(node);
|
|
2382
2601
|
}
|
|
2383
|
-
// if root scene is not a renderer node,
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
if (node[NGT_PORTAL_CONTENT_FLAG] &&
|
|
2390
|
-
node[NGT_DOM_PARENT_FLAG] &&
|
|
2391
|
-
isRendererNode(node[NGT_DOM_PARENT_FLAG])) {
|
|
2392
|
-
const portalContentParent = node[NGT_DOM_PARENT_FLAG];
|
|
2602
|
+
// if root scene is not a Three renderer node, make it one here
|
|
2603
|
+
const rendererRootScene = isRendererNodeType(rootScene, 'three')
|
|
2604
|
+
? rootScene
|
|
2605
|
+
: createRendererNode('three', rootScene, this.document);
|
|
2606
|
+
if (anchor.kind === 'portal' && anchor.domParent && isRendererNode(anchor.domParent)) {
|
|
2607
|
+
const portalContentParent = anchor.domParent;
|
|
2393
2608
|
const portalContentParentRS = portalContentParent.__ngt_renderer__;
|
|
2394
|
-
|
|
2395
|
-
portalContentParentRS[3 /* NgtRendererClassId.portalContainer */] = rootScene;
|
|
2396
|
-
}
|
|
2609
|
+
portalContentParentRS[3 /* NgtRendererClassId.portalContainer */] = rendererRootScene;
|
|
2397
2610
|
}
|
|
2398
|
-
return
|
|
2611
|
+
return rendererRootScene;
|
|
2399
2612
|
}
|
|
2400
|
-
const rendererParentNode = node.__ngt_renderer__?.[
|
|
2613
|
+
const rendererParentNode = node.__ngt_renderer__?.[4 /* NgtRendererClassId.parent */];
|
|
2401
2614
|
// returns the renderer parent node if it exists, otherwise returns the delegateRenderer parentNode
|
|
2402
2615
|
return rendererParentNode ?? this.delegateRenderer.parentNode(node);
|
|
2403
2616
|
}
|
|
@@ -2406,6 +2619,15 @@ class NgtRenderer2 {
|
|
|
2406
2619
|
if (!rS || rS[1 /* NgtRendererClassId.destroyed */])
|
|
2407
2620
|
return this.delegateRenderer.removeAttribute(el, name, namespace);
|
|
2408
2621
|
if (rS[0 /* NgtRendererClassId.type */] === 'three') {
|
|
2622
|
+
const instanceState = getInstanceState(el);
|
|
2623
|
+
const parent = (instanceState?.parent ? untracked(instanceState.parent) : null) ?? this.findTargetThreeParent(el);
|
|
2624
|
+
if (name === 'attach') {
|
|
2625
|
+
this.updateAttachment(el, undefined, parent);
|
|
2626
|
+
return;
|
|
2627
|
+
}
|
|
2628
|
+
applyProps(el, { [name]: undefined });
|
|
2629
|
+
if (name === 'geometry')
|
|
2630
|
+
untracked(() => instanceState?.updateGeometryStamp());
|
|
2409
2631
|
return;
|
|
2410
2632
|
}
|
|
2411
2633
|
return this.delegateRenderer.removeAttribute(el, name, namespace);
|
|
@@ -2421,12 +2643,9 @@ class NgtRenderer2 {
|
|
|
2421
2643
|
}
|
|
2422
2644
|
if (rS[0 /* NgtRendererClassId.type */] === 'three') {
|
|
2423
2645
|
if (name === 'attach') {
|
|
2424
|
-
const
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
if (instanceState)
|
|
2428
|
-
instanceState.attach = paths;
|
|
2429
|
-
}
|
|
2646
|
+
const instanceState = getInstanceState(el);
|
|
2647
|
+
const parent = (instanceState?.parent ? untracked(instanceState.parent) : null) ?? this.findTargetThreeParent(el);
|
|
2648
|
+
this.updateAttachment(el, value, parent);
|
|
2430
2649
|
return;
|
|
2431
2650
|
}
|
|
2432
2651
|
// coercion for primitive values
|
|
@@ -2453,44 +2672,55 @@ class NgtRenderer2 {
|
|
|
2453
2672
|
// NOTE: untrack all signal updates because this is during setProperty which is a reactive context
|
|
2454
2673
|
// attaching potentially updates signals which is not allowed
|
|
2455
2674
|
const rS = el.__ngt_renderer__;
|
|
2456
|
-
if (!rS
|
|
2675
|
+
if (!rS)
|
|
2676
|
+
return this.delegateRenderer.setProperty(el, name, value);
|
|
2677
|
+
if (rS[1 /* NgtRendererClassId.destroyed */]) {
|
|
2457
2678
|
this.options.verbose &&
|
|
2458
2679
|
console.warn('[NGT dev mode] setProperty is invoked on destroyed renderer node.', { el, name, value });
|
|
2459
2680
|
return;
|
|
2460
2681
|
}
|
|
2461
|
-
if (
|
|
2682
|
+
if (isRendererNodeType(el, 'three')) {
|
|
2683
|
+
const threeState = el.__ngt_renderer__;
|
|
2462
2684
|
const instanceState = getInstanceState(el);
|
|
2463
|
-
const
|
|
2685
|
+
const attachedParent = instanceState?.hierarchyStore.snapshot.parent ?? null;
|
|
2686
|
+
const parent = attachedParent ?? this.findTargetThreeParent(el);
|
|
2464
2687
|
if (name === 'parameters') {
|
|
2688
|
+
const parameters = value && typeof value === 'object' ? { ...value } : {};
|
|
2465
2689
|
// NOTE: short-cut for null raycast to prevent upstream from creating a nullRaycast property
|
|
2466
|
-
if ('raycast' in
|
|
2467
|
-
|
|
2690
|
+
if ('raycast' in parameters && parameters['raycast'] === null) {
|
|
2691
|
+
parameters['raycast'] = () => null;
|
|
2468
2692
|
}
|
|
2469
|
-
|
|
2470
|
-
|
|
2693
|
+
const previousKeys = this.parameterKeys.get(el) ?? new Set();
|
|
2694
|
+
const nextKeys = new Set(Object.keys(parameters));
|
|
2695
|
+
const removedParameters = Object.fromEntries([...previousKeys]
|
|
2696
|
+
.filter((key) => key !== 'attach' && !nextKeys.has(key))
|
|
2697
|
+
.map((key) => [key, undefined]));
|
|
2698
|
+
const { attach: nextAttach, ...nextParameters } = parameters;
|
|
2699
|
+
applyProps(el, { ...removedParameters, ...nextParameters });
|
|
2700
|
+
this.parameterKeys.set(el, nextKeys);
|
|
2701
|
+
if (nextKeys.has('geometry') || (previousKeys.has('geometry') && !nextKeys.has('geometry'))) {
|
|
2471
2702
|
untracked(() => instanceState?.updateGeometryStamp());
|
|
2472
2703
|
}
|
|
2473
|
-
if ('attach' in
|
|
2474
|
-
|
|
2475
|
-
instanceState.attach = this.normalizeAttach(value['attach']);
|
|
2476
|
-
if (parent)
|
|
2477
|
-
untracked(() => attachThreeNodes(parent, el));
|
|
2704
|
+
if ('attach' in parameters || previousKeys.has('attach')) {
|
|
2705
|
+
this.updateAttachment(el, nextAttach, parent);
|
|
2478
2706
|
}
|
|
2479
2707
|
return;
|
|
2480
2708
|
}
|
|
2481
2709
|
// [rawValue]
|
|
2482
2710
|
if (instanceState?.type === 'ngt-value' && name === 'rawValue') {
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2711
|
+
untracked(() => {
|
|
2712
|
+
if (attachedParent)
|
|
2713
|
+
removeThreeChild(el, attachedParent, false);
|
|
2714
|
+
threeState[2 /* NgtRendererClassId.rawValue */] = value;
|
|
2715
|
+
if (parent) {
|
|
2716
|
+
attachThreeNodes(parent, el, undefined, instanceState.store ?? undefined);
|
|
2717
|
+
}
|
|
2718
|
+
});
|
|
2486
2719
|
return;
|
|
2487
2720
|
}
|
|
2488
2721
|
// [attach]
|
|
2489
2722
|
if (name === 'attach') {
|
|
2490
|
-
|
|
2491
|
-
instanceState.attach = this.normalizeAttach(value);
|
|
2492
|
-
if (parent)
|
|
2493
|
-
untracked(() => attachThreeNodes(parent, el));
|
|
2723
|
+
this.updateAttachment(el, value, parent);
|
|
2494
2724
|
return;
|
|
2495
2725
|
}
|
|
2496
2726
|
// NOTE: short-cut for null raycast to prevent upstream from creating a nullRaycast property
|
|
@@ -2498,7 +2728,7 @@ class NgtRenderer2 {
|
|
|
2498
2728
|
value = () => null;
|
|
2499
2729
|
}
|
|
2500
2730
|
applyProps(el, { [name]: value });
|
|
2501
|
-
if (instanceState && name === 'geometry'
|
|
2731
|
+
if (instanceState && name === 'geometry') {
|
|
2502
2732
|
untracked(() => {
|
|
2503
2733
|
instanceState.updateGeometryStamp();
|
|
2504
2734
|
});
|
|
@@ -2507,13 +2737,13 @@ class NgtRenderer2 {
|
|
|
2507
2737
|
}
|
|
2508
2738
|
return this.delegateRenderer.setProperty(el, name, value);
|
|
2509
2739
|
}
|
|
2510
|
-
listen(target, eventName, callback) {
|
|
2740
|
+
listen(target, eventName, callback, options) {
|
|
2511
2741
|
if (typeof target === 'string') {
|
|
2512
|
-
return this.delegateRenderer.listen(target, eventName, callback);
|
|
2742
|
+
return this.delegateRenderer.listen(target, eventName, callback, options);
|
|
2513
2743
|
}
|
|
2514
2744
|
const rS = target.__ngt_renderer__;
|
|
2515
2745
|
if (!rS) {
|
|
2516
|
-
return this.delegateRenderer.listen(target, eventName, callback);
|
|
2746
|
+
return this.delegateRenderer.listen(target, eventName, callback, options);
|
|
2517
2747
|
}
|
|
2518
2748
|
if (rS[1 /* NgtRendererClassId.destroyed */])
|
|
2519
2749
|
return () => { };
|
|
@@ -2528,43 +2758,34 @@ class NgtRenderer2 {
|
|
|
2528
2758
|
return () => { };
|
|
2529
2759
|
}
|
|
2530
2760
|
if (eventName === 'attached') {
|
|
2531
|
-
|
|
2532
|
-
const parent = iS.parent && untracked(iS.parent);
|
|
2533
|
-
if (parent)
|
|
2534
|
-
iS.onAttach({ parent, node: target });
|
|
2535
|
-
return () => {
|
|
2536
|
-
iS.onAttach = undefined;
|
|
2537
|
-
};
|
|
2761
|
+
return this.registerWithOptions(callback, options, (listener) => this.listenToInstanceEvent(target, 'attached', listener));
|
|
2538
2762
|
}
|
|
2539
2763
|
if (eventName === 'updated') {
|
|
2540
|
-
|
|
2541
|
-
return () => {
|
|
2542
|
-
iS.onUpdate = undefined;
|
|
2543
|
-
};
|
|
2764
|
+
return this.registerWithOptions(callback, options, (listener) => this.listenToInstanceEvent(target, 'updated', listener));
|
|
2544
2765
|
}
|
|
2545
2766
|
if (THREE_NATIVE_EVENTS.includes(eventName) && target instanceof THREE.EventDispatcher) {
|
|
2546
2767
|
// NOTE: rename to dispose because that's the event type, not disposed.
|
|
2547
2768
|
if (eventName === 'disposed') {
|
|
2548
2769
|
eventName = 'dispose';
|
|
2549
2770
|
}
|
|
2550
|
-
|
|
2551
|
-
(eventName
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
};
|
|
2771
|
+
return this.registerWithOptions(callback, options, (listener) => {
|
|
2772
|
+
target.addEventListener(eventName, listener);
|
|
2773
|
+
if (target.parent && eventName === 'added') {
|
|
2774
|
+
listener({ type: eventName, target });
|
|
2775
|
+
}
|
|
2776
|
+
return () => target.removeEventListener(eventName, listener);
|
|
2777
|
+
});
|
|
2558
2778
|
}
|
|
2559
|
-
const cleanup = iS.setPointerEvent?.(eventName,
|
|
2779
|
+
const cleanup = this.registerWithOptions(callback, options, (listener) => iS.setPointerEvent?.(eventName, listener) || (() => { }));
|
|
2560
2780
|
// this means the object has already been attached to the parent and has its store propagated
|
|
2561
2781
|
if (iS.store)
|
|
2562
2782
|
iS.addInteraction?.(iS.store);
|
|
2563
2783
|
return cleanup;
|
|
2564
2784
|
}
|
|
2565
|
-
return this.delegateRenderer.listen(target, eventName, callback);
|
|
2785
|
+
return this.delegateRenderer.listen(target, eventName, callback, options);
|
|
2566
2786
|
}
|
|
2567
|
-
appendThreeRendererNodes(parent, child) {
|
|
2787
|
+
appendThreeRendererNodes(parent, child, refChild, storeOverride) {
|
|
2788
|
+
parent = child.__ngt_renderer__[6 /* NgtRendererClassId.parentOverride */] ?? parent;
|
|
2568
2789
|
// if parent and child are the same, skip
|
|
2569
2790
|
if (parent === child) {
|
|
2570
2791
|
this.options.verbose &&
|
|
@@ -2574,31 +2795,226 @@ class NgtRenderer2 {
|
|
|
2574
2795
|
});
|
|
2575
2796
|
return;
|
|
2576
2797
|
}
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
});
|
|
2585
|
-
return;
|
|
2798
|
+
if (!is.instance(parent) || !is.instance(child)) {
|
|
2799
|
+
throw new Error('[NGT] THREE renderer nodes need to be prepared with local instance state.');
|
|
2800
|
+
}
|
|
2801
|
+
const childState = getInstanceState(child);
|
|
2802
|
+
const currentParent = childState?.parent ? untracked(childState.parent) : null;
|
|
2803
|
+
if (currentParent && currentParent !== parent) {
|
|
2804
|
+
removeThreeChild(child, currentParent, false);
|
|
2586
2805
|
}
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
attachThreeNodes(parent, child);
|
|
2806
|
+
const before = this.findThreeInsertionReference(parent, child, refChild);
|
|
2807
|
+
const anchor = getRendererAnchor(refChild);
|
|
2808
|
+
const attachmentStore = storeOverride ?? (anchor?.kind === 'canvas' || anchor?.kind === 'portal' ? anchor.store : undefined);
|
|
2809
|
+
attachThreeNodes(parent, child, is.instance(before) ? before : null, attachmentStore);
|
|
2591
2810
|
return;
|
|
2592
2811
|
}
|
|
2593
|
-
setNodeRelationship(parent, child) {
|
|
2594
|
-
|
|
2595
|
-
|
|
2812
|
+
setNodeRelationship(parent, child, refChild) {
|
|
2813
|
+
const anchor = getRendererAnchor(refChild);
|
|
2814
|
+
const logicalParent = anchor?.kind === 'portal' && anchor.domParent && isRendererNode(anchor.domParent)
|
|
2815
|
+
? anchor.domParent
|
|
2816
|
+
: parent;
|
|
2817
|
+
insertRendererChildNode(logicalParent, child, refChild);
|
|
2818
|
+
}
|
|
2819
|
+
findNearestThreeParent(node) {
|
|
2820
|
+
let current = node;
|
|
2821
|
+
while (current && isRendererNode(current)) {
|
|
2822
|
+
const state = current.__ngt_renderer__;
|
|
2823
|
+
if (state[0 /* NgtRendererClassId.type */] === 'three')
|
|
2824
|
+
return current;
|
|
2825
|
+
if (state[0 /* NgtRendererClassId.type */] === 'portal' && state[3 /* NgtRendererClassId.portalContainer */]) {
|
|
2826
|
+
return state[3 /* NgtRendererClassId.portalContainer */];
|
|
2827
|
+
}
|
|
2828
|
+
current = state[4 /* NgtRendererClassId.parent */];
|
|
2829
|
+
}
|
|
2830
|
+
return null;
|
|
2831
|
+
}
|
|
2832
|
+
findTargetThreeParent(node) {
|
|
2833
|
+
if (!isRendererNodeType(node, 'three'))
|
|
2834
|
+
return null;
|
|
2835
|
+
const state = node.__ngt_renderer__;
|
|
2836
|
+
const parentOverride = state[6 /* NgtRendererClassId.parentOverride */];
|
|
2837
|
+
if (parentOverride && is.instance(parentOverride))
|
|
2838
|
+
return parentOverride;
|
|
2839
|
+
const logicalParent = this.findNearestThreeParent(state[4 /* NgtRendererClassId.parent */]);
|
|
2840
|
+
return logicalParent && is.instance(logicalParent) ? logicalParent : null;
|
|
2841
|
+
}
|
|
2842
|
+
portalStore(node) {
|
|
2843
|
+
const anchor = getRendererAnchor(node);
|
|
2844
|
+
return anchor?.kind === 'portal' ? anchor.store : undefined;
|
|
2845
|
+
}
|
|
2846
|
+
findNearestPortalStore(node) {
|
|
2847
|
+
let current = node;
|
|
2848
|
+
while (current && isRendererNode(current)) {
|
|
2849
|
+
const store = this.portalStore(current);
|
|
2850
|
+
if (store)
|
|
2851
|
+
return store;
|
|
2852
|
+
current = current.__ngt_renderer__[4 /* NgtRendererClassId.parent */];
|
|
2853
|
+
}
|
|
2854
|
+
return undefined;
|
|
2855
|
+
}
|
|
2856
|
+
findFirstThreeDescendant(node, physicalParent) {
|
|
2857
|
+
if (!node || !isRendererNode(node))
|
|
2858
|
+
return null;
|
|
2859
|
+
const state = node.__ngt_renderer__;
|
|
2860
|
+
if (isRendererNodeType(node, 'three')) {
|
|
2861
|
+
if (!is.instance(node) || !is.instance(physicalParent))
|
|
2862
|
+
return null;
|
|
2863
|
+
if (!is.three(node, 'isObject3D'))
|
|
2864
|
+
return null;
|
|
2865
|
+
if (!is.three(physicalParent, 'isObject3D'))
|
|
2866
|
+
return null;
|
|
2867
|
+
const instanceState = getInstanceState(node);
|
|
2868
|
+
if (instanceState?.parent && untracked(instanceState.parent) === physicalParent) {
|
|
2869
|
+
if (physicalParent.children.includes(node))
|
|
2870
|
+
return node;
|
|
2871
|
+
}
|
|
2872
|
+
return null;
|
|
2873
|
+
}
|
|
2874
|
+
for (const child of state[5 /* NgtRendererClassId.children */]) {
|
|
2875
|
+
const descendant = this.findFirstThreeDescendant(child, physicalParent);
|
|
2876
|
+
if (descendant)
|
|
2877
|
+
return descendant;
|
|
2878
|
+
}
|
|
2879
|
+
return null;
|
|
2880
|
+
}
|
|
2881
|
+
findThreeInsertionReference(physicalParent, child, requestedRef) {
|
|
2882
|
+
const directReference = this.findFirstThreeDescendant(requestedRef, physicalParent);
|
|
2883
|
+
if (directReference)
|
|
2884
|
+
return directReference;
|
|
2885
|
+
return this.findThreeReferenceAfterLogicalNode(physicalParent, child);
|
|
2886
|
+
}
|
|
2887
|
+
findThreeReferenceAfterLogicalNode(physicalParent, node) {
|
|
2888
|
+
let current = node;
|
|
2889
|
+
while (current) {
|
|
2890
|
+
const logicalParent = current.__ngt_renderer__[4 /* NgtRendererClassId.parent */];
|
|
2891
|
+
if (!logicalParent || !isRendererNode(logicalParent))
|
|
2892
|
+
return null;
|
|
2893
|
+
const siblings = logicalParent.__ngt_renderer__[5 /* NgtRendererClassId.children */];
|
|
2894
|
+
const currentIndex = siblings.indexOf(current);
|
|
2895
|
+
for (let index = currentIndex + 1; index < siblings.length; index++) {
|
|
2896
|
+
const reference = this.findFirstThreeDescendant(siblings[index], physicalParent);
|
|
2897
|
+
if (reference)
|
|
2898
|
+
return reference;
|
|
2899
|
+
}
|
|
2900
|
+
if (logicalParent === physicalParent)
|
|
2901
|
+
return null;
|
|
2902
|
+
current = logicalParent;
|
|
2903
|
+
}
|
|
2904
|
+
return null;
|
|
2905
|
+
}
|
|
2906
|
+
attachThreeDescendants(parent, node, refChild, storeOverride) {
|
|
2907
|
+
for (const child of node.__ngt_renderer__[5 /* NgtRendererClassId.children */]) {
|
|
2908
|
+
if (child.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three') {
|
|
2909
|
+
this.appendThreeRendererNodes(parent, child, refChild, storeOverride);
|
|
2910
|
+
}
|
|
2911
|
+
else {
|
|
2912
|
+
this.attachThreeDescendants(parent, child, refChild, storeOverride);
|
|
2913
|
+
}
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
detachThreeDescendants(node) {
|
|
2917
|
+
for (const child of node.__ngt_renderer__[5 /* NgtRendererClassId.children */]) {
|
|
2918
|
+
if (child.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three') {
|
|
2919
|
+
const instanceState = getInstanceState(child);
|
|
2920
|
+
const physicalParent = instanceState?.parent ? untracked(instanceState.parent) : null;
|
|
2921
|
+
if (physicalParent) {
|
|
2922
|
+
removeThreeChild(child, physicalParent, false);
|
|
2923
|
+
}
|
|
2924
|
+
}
|
|
2925
|
+
else {
|
|
2926
|
+
this.detachThreeDescendants(child);
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
destroyOwnedNodes(host) {
|
|
2931
|
+
const ownedNodes = host.__ngt_renderer__[9 /* NgtRendererClassId.ownedNodes */];
|
|
2932
|
+
if (!ownedNodes)
|
|
2933
|
+
return;
|
|
2934
|
+
for (const ownedNode of [...ownedNodes]) {
|
|
2935
|
+
releaseRendererOwner(ownedNode, false);
|
|
2936
|
+
internalDestroyNode(ownedNode, this.removeChild.bind(this));
|
|
2937
|
+
}
|
|
2938
|
+
ownedNodes.clear();
|
|
2939
|
+
}
|
|
2940
|
+
listenToInstanceEvent(target, eventName, callback) {
|
|
2941
|
+
const instanceState = getInstanceState(target);
|
|
2942
|
+
if (!instanceState)
|
|
2943
|
+
return () => { };
|
|
2944
|
+
const registry = eventName === 'attached' ? this.attachedListeners : this.updatedListeners;
|
|
2945
|
+
let bucket = registry.get(target);
|
|
2946
|
+
if (!bucket) {
|
|
2947
|
+
const listeners = new Map();
|
|
2948
|
+
const existing = eventName === 'attached' ? instanceState.onAttach : instanceState.onUpdate;
|
|
2949
|
+
if (existing)
|
|
2950
|
+
listeners.set(Symbol('existing'), existing);
|
|
2951
|
+
const dispatch = (event) => {
|
|
2952
|
+
for (const listener of [...listeners.values()])
|
|
2953
|
+
listener(event);
|
|
2954
|
+
};
|
|
2955
|
+
bucket = { listeners, dispatch };
|
|
2956
|
+
registry.set(target, bucket);
|
|
2957
|
+
if (eventName === 'attached')
|
|
2958
|
+
instanceState.onAttach = dispatch;
|
|
2959
|
+
else
|
|
2960
|
+
instanceState.onUpdate = dispatch;
|
|
2961
|
+
}
|
|
2962
|
+
const token = Symbol(eventName);
|
|
2963
|
+
bucket.listeners.set(token, callback);
|
|
2964
|
+
if (eventName === 'attached') {
|
|
2965
|
+
const parent = instanceState.parent && untracked(instanceState.parent);
|
|
2966
|
+
if (parent)
|
|
2967
|
+
callback({ parent, node: target });
|
|
2968
|
+
}
|
|
2969
|
+
let active = true;
|
|
2970
|
+
return () => {
|
|
2971
|
+
if (!active)
|
|
2972
|
+
return;
|
|
2973
|
+
active = false;
|
|
2974
|
+
if (!bucket?.listeners.delete(token) || bucket.listeners.size > 0)
|
|
2975
|
+
return;
|
|
2976
|
+
registry.delete(target);
|
|
2977
|
+
if (eventName === 'attached' && instanceState.onAttach === bucket.dispatch) {
|
|
2978
|
+
instanceState.onAttach = undefined;
|
|
2979
|
+
}
|
|
2980
|
+
else if (eventName === 'updated' && instanceState.onUpdate === bucket.dispatch) {
|
|
2981
|
+
instanceState.onUpdate = undefined;
|
|
2982
|
+
}
|
|
2983
|
+
};
|
|
2596
2984
|
}
|
|
2597
|
-
|
|
2985
|
+
registerWithOptions(callback, options, register) {
|
|
2986
|
+
if (!options?.once)
|
|
2987
|
+
return register(callback);
|
|
2988
|
+
let cleanup;
|
|
2989
|
+
let cleanupPending = false;
|
|
2990
|
+
let active = true;
|
|
2991
|
+
const onceCallback = (event) => {
|
|
2992
|
+
if (!active)
|
|
2993
|
+
return;
|
|
2994
|
+
active = false;
|
|
2995
|
+
try {
|
|
2996
|
+
return callback(event);
|
|
2997
|
+
}
|
|
2998
|
+
finally {
|
|
2999
|
+
if (cleanup)
|
|
3000
|
+
cleanup();
|
|
3001
|
+
else
|
|
3002
|
+
cleanupPending = true;
|
|
3003
|
+
}
|
|
3004
|
+
};
|
|
3005
|
+
cleanup = register(onceCallback);
|
|
3006
|
+
if (cleanupPending)
|
|
3007
|
+
cleanup();
|
|
3008
|
+
return () => {
|
|
3009
|
+
active = false;
|
|
3010
|
+
cleanup?.();
|
|
3011
|
+
};
|
|
3012
|
+
}
|
|
3013
|
+
getNgtDirective(directive) {
|
|
2598
3014
|
let directiveInstance;
|
|
2599
|
-
let i =
|
|
3015
|
+
let i = this.directiveInjectors.length - 1;
|
|
2600
3016
|
while (i >= 0) {
|
|
2601
|
-
const injector =
|
|
3017
|
+
const injector = this.directiveInjectors[i];
|
|
2602
3018
|
const instance = injector.get(directive, null);
|
|
2603
3019
|
if (instance && typeof instance === 'object' && instance.validate()) {
|
|
2604
3020
|
directiveInstance = instance;
|
|
@@ -2609,12 +3025,75 @@ class NgtRenderer2 {
|
|
|
2609
3025
|
return directiveInstance;
|
|
2610
3026
|
}
|
|
2611
3027
|
normalizeAttach(attach) {
|
|
3028
|
+
if (attach == null)
|
|
3029
|
+
return undefined;
|
|
2612
3030
|
if (typeof attach === 'function')
|
|
2613
3031
|
return attach;
|
|
2614
3032
|
if (typeof attach === 'string')
|
|
2615
3033
|
return attach.split('.');
|
|
2616
3034
|
return attach.flatMap((item) => item.toString().split('.'));
|
|
2617
3035
|
}
|
|
3036
|
+
updateAttachment(node, attach, parent) {
|
|
3037
|
+
untracked(() => {
|
|
3038
|
+
const instanceState = getInstanceState(node);
|
|
3039
|
+
if (!instanceState)
|
|
3040
|
+
return;
|
|
3041
|
+
const nextAttach = this.normalizeAttach(attach);
|
|
3042
|
+
const currentAttach = instanceState.attach;
|
|
3043
|
+
const isSameAttach = currentAttach === nextAttach ||
|
|
3044
|
+
(Array.isArray(currentAttach) &&
|
|
3045
|
+
Array.isArray(nextAttach) &&
|
|
3046
|
+
currentAttach.length === nextAttach.length &&
|
|
3047
|
+
currentAttach.every((part, index) => part === nextAttach[index]));
|
|
3048
|
+
if (isSameAttach)
|
|
3049
|
+
return;
|
|
3050
|
+
const attachedParent = instanceState.hierarchyStore.snapshot.parent;
|
|
3051
|
+
if (attachedParent)
|
|
3052
|
+
removeThreeChild(node, attachedParent, false);
|
|
3053
|
+
instanceState.attach = nextAttach;
|
|
3054
|
+
if (parent) {
|
|
3055
|
+
attachThreeNodes(parent, node, undefined, instanceState.store ?? undefined);
|
|
3056
|
+
}
|
|
3057
|
+
});
|
|
3058
|
+
}
|
|
3059
|
+
addClass(el, name) {
|
|
3060
|
+
if (el?.__ngt_renderer__?.[0 /* NgtRendererClassId.type */] === 'three')
|
|
3061
|
+
return;
|
|
3062
|
+
this.delegateRenderer.addClass(el, name);
|
|
3063
|
+
}
|
|
3064
|
+
removeClass(el, name) {
|
|
3065
|
+
if (el?.__ngt_renderer__?.[0 /* NgtRendererClassId.type */] === 'three')
|
|
3066
|
+
return;
|
|
3067
|
+
this.delegateRenderer.removeClass(el, name);
|
|
3068
|
+
}
|
|
3069
|
+
setStyle(el, style, value, flags) {
|
|
3070
|
+
if (el?.__ngt_renderer__?.[0 /* NgtRendererClassId.type */] === 'three')
|
|
3071
|
+
return;
|
|
3072
|
+
this.delegateRenderer.setStyle(el, style, value, flags);
|
|
3073
|
+
}
|
|
3074
|
+
removeStyle(el, style, flags) {
|
|
3075
|
+
if (el?.__ngt_renderer__?.[0 /* NgtRendererClassId.type */] === 'three')
|
|
3076
|
+
return;
|
|
3077
|
+
this.delegateRenderer.removeStyle(el, style, flags);
|
|
3078
|
+
}
|
|
3079
|
+
selectRootElement(selectorOrNode, preserveContent) {
|
|
3080
|
+
return this.delegateRenderer.selectRootElement(selectorOrNode, preserveContent);
|
|
3081
|
+
}
|
|
3082
|
+
nextSibling(node) {
|
|
3083
|
+
if (isRendererNode(node)) {
|
|
3084
|
+
const state = node.__ngt_renderer__;
|
|
3085
|
+
if (state[4 /* NgtRendererClassId.parent */])
|
|
3086
|
+
return getRendererNextSibling(node);
|
|
3087
|
+
if (state[0 /* NgtRendererClassId.type */] === 'three')
|
|
3088
|
+
return null;
|
|
3089
|
+
}
|
|
3090
|
+
return this.delegateRenderer.nextSibling(node);
|
|
3091
|
+
}
|
|
3092
|
+
setValue(node, value) {
|
|
3093
|
+
if (node?.__ngt_renderer__?.[0 /* NgtRendererClassId.type */] === 'three')
|
|
3094
|
+
return;
|
|
3095
|
+
this.delegateRenderer.setValue(node, value);
|
|
3096
|
+
}
|
|
2618
3097
|
}
|
|
2619
3098
|
|
|
2620
3099
|
/**
|
|
@@ -2643,6 +3122,12 @@ function storeFactory() {
|
|
|
2643
3122
|
const defaultTarget = new THREE.Vector3();
|
|
2644
3123
|
const tempTarget = new THREE.Vector3();
|
|
2645
3124
|
let performanceTimeout = undefined;
|
|
3125
|
+
const cancelPerformanceRegression = () => {
|
|
3126
|
+
if (performanceTimeout === undefined)
|
|
3127
|
+
return;
|
|
3128
|
+
clearTimeout(performanceTimeout);
|
|
3129
|
+
performanceTimeout = undefined;
|
|
3130
|
+
};
|
|
2646
3131
|
const pointer = new THREE.Vector2();
|
|
2647
3132
|
// getCurrentViewport will mutate this instead of creating a new object everytime
|
|
2648
3133
|
const tempViewport = {
|
|
@@ -2682,17 +3167,19 @@ function storeFactory() {
|
|
|
2682
3167
|
regress: () => {
|
|
2683
3168
|
const state = store.snapshot;
|
|
2684
3169
|
// Clear timeout
|
|
2685
|
-
|
|
2686
|
-
clearTimeout(performanceTimeout);
|
|
3170
|
+
cancelPerformanceRegression();
|
|
2687
3171
|
// Set lower bound performance
|
|
2688
3172
|
if (state.performance.current !== state.performance.min)
|
|
2689
3173
|
store.update((state) => ({
|
|
2690
3174
|
performance: { ...state.performance, current: state.performance.min },
|
|
2691
3175
|
}));
|
|
2692
3176
|
// Go back to upper bound performance after a while unless something regresses meanwhile
|
|
2693
|
-
performanceTimeout = setTimeout(() =>
|
|
2694
|
-
|
|
2695
|
-
|
|
3177
|
+
performanceTimeout = setTimeout(() => {
|
|
3178
|
+
performanceTimeout = undefined;
|
|
3179
|
+
store.update((state) => ({
|
|
3180
|
+
performance: { ...state.performance, current: store.snapshot.performance.max },
|
|
3181
|
+
}));
|
|
3182
|
+
}, state.performance.debounce);
|
|
2696
3183
|
},
|
|
2697
3184
|
},
|
|
2698
3185
|
size: { width: 0, height: 0, top: 0, left: 0 },
|
|
@@ -2780,27 +3267,34 @@ function storeFactory() {
|
|
|
2780
3267
|
subscribers: [],
|
|
2781
3268
|
subscribe: (callback, priority = 0, _store = store) => {
|
|
2782
3269
|
const internal = _store.snapshot.internal;
|
|
3270
|
+
const subscription = { callback, priority, store: _store };
|
|
2783
3271
|
// If this subscription was given a priority, it takes rendering into its own hands
|
|
2784
3272
|
// For that reason we switch off automatic rendering and increase the manual flag
|
|
2785
3273
|
// As long as this flag is positive there can be no internal rendering at all
|
|
2786
3274
|
// because there could be multiple render subscriptions
|
|
2787
|
-
internal.priority
|
|
2788
|
-
internal.subscribers.push({ callback, priority, store: _store });
|
|
3275
|
+
internal.priority += priority > 0 ? 1 : 0;
|
|
2789
3276
|
// Register subscriber and sort layers from lowest to highest, meaning,
|
|
2790
3277
|
// highest priority renders last (on top of the other frames)
|
|
2791
|
-
internal.subscribers = internal.subscribers.sort((a, b) => (a.priority || 0) - (b.priority || 0));
|
|
3278
|
+
internal.subscribers = [...internal.subscribers, subscription].sort((a, b) => (a.priority || 0) - (b.priority || 0));
|
|
3279
|
+
let active = true;
|
|
2792
3280
|
return () => {
|
|
3281
|
+
if (!active)
|
|
3282
|
+
return;
|
|
3283
|
+
active = false;
|
|
2793
3284
|
const internal = _store.snapshot.internal;
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
3285
|
+
const index = internal?.subscribers.indexOf(subscription);
|
|
3286
|
+
if (index == null || index < 0)
|
|
3287
|
+
return;
|
|
3288
|
+
internal.subscribers = internal.subscribers.filter((record) => record !== subscription);
|
|
3289
|
+
if (priority > 0)
|
|
3290
|
+
internal.priority = Math.max(0, internal.priority - 1);
|
|
2800
3291
|
};
|
|
2801
3292
|
},
|
|
2802
3293
|
},
|
|
2803
3294
|
});
|
|
3295
|
+
Object.defineProperty(store.snapshot.performance.regress, 'cancel', {
|
|
3296
|
+
value: cancelPerformanceRegression,
|
|
3297
|
+
});
|
|
2804
3298
|
Object.defineProperty(store, '__pointerMissed$', { get: () => pointerMissed$ });
|
|
2805
3299
|
let { size: oldSize, viewport: { dpr: oldDpr }, camera: oldCamera, } = store.snapshot;
|
|
2806
3300
|
effect(() => {
|
|
@@ -2914,22 +3408,11 @@ class NgtParent extends NgtCommonDirective {
|
|
|
2914
3408
|
this.linkedValue = linkedSignal(this._parent, ...(ngDevMode ? [{ debugName: "linkedValue" }] : /* istanbul ignore next */ []));
|
|
2915
3409
|
this.shouldSkipRender = computed(() => !this._parent(), ...(ngDevMode ? [{ debugName: "shouldSkipRender" }] : /* istanbul ignore next */ []));
|
|
2916
3410
|
const commentNode = this.commentNode;
|
|
2917
|
-
commentNode.
|
|
2918
|
-
commentNode[NGT_PARENT_FLAG] = true;
|
|
2919
|
-
if (commentNode[NGT_INTERNAL_ADD_COMMENT_FLAG]) {
|
|
2920
|
-
commentNode[NGT_INTERNAL_ADD_COMMENT_FLAG]('parent', this.injector);
|
|
2921
|
-
delete commentNode[NGT_INTERNAL_ADD_COMMENT_FLAG];
|
|
2922
|
-
}
|
|
3411
|
+
setRendererAnchor(commentNode, { kind: 'parent', injector: this.injector });
|
|
2923
3412
|
}
|
|
2924
3413
|
validate() {
|
|
2925
3414
|
return !this.injected && !!this.injectedValue;
|
|
2926
3415
|
}
|
|
2927
|
-
beforeCreateView() {
|
|
2928
|
-
const commentNode = this.commentNode;
|
|
2929
|
-
if (commentNode[NGT_INTERNAL_SET_PARENT_COMMENT_FLAG]) {
|
|
2930
|
-
commentNode[NGT_INTERNAL_SET_PARENT_COMMENT_FLAG](this.injectedValue);
|
|
2931
|
-
}
|
|
2932
|
-
}
|
|
2933
3416
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: NgtParent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2934
3417
|
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: NgtParent, isStandalone: true, selector: "ng-template[parent]", inputs: { parent: { classPropertyName: "parent", publicName: "parent", isSignal: true, isRequired: true, transformFunction: null } }, usesInheritance: true, ngImport: i0 }); }
|
|
2935
3418
|
}
|
|
@@ -3095,8 +3578,135 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
3095
3578
|
type: Directive
|
|
3096
3579
|
}], ctorParameters: () => [] });
|
|
3097
3580
|
|
|
3098
|
-
const
|
|
3099
|
-
|
|
3581
|
+
const defaultConfigurationKey = Symbol('ngt-default-loader-configuration');
|
|
3582
|
+
/**
|
|
3583
|
+
* Interns semantic tuples component-by-component so equivalent configuration
|
|
3584
|
+
* and multi-part URL tuples share an identity without retaining object parts.
|
|
3585
|
+
*/
|
|
3586
|
+
class SemanticKeyRegistry {
|
|
3587
|
+
constructor() {
|
|
3588
|
+
this.root = new SemanticKeyNode();
|
|
3589
|
+
}
|
|
3590
|
+
resolve(key) {
|
|
3591
|
+
if (!Array.isArray(key))
|
|
3592
|
+
return key;
|
|
3593
|
+
let node = this.root;
|
|
3594
|
+
for (const part of key) {
|
|
3595
|
+
node = node.child(part);
|
|
3596
|
+
}
|
|
3597
|
+
return (node.value ??= {});
|
|
3598
|
+
}
|
|
3599
|
+
clear() {
|
|
3600
|
+
this.root = new SemanticKeyNode();
|
|
3601
|
+
}
|
|
3602
|
+
}
|
|
3603
|
+
class SemanticKeyNode {
|
|
3604
|
+
constructor() {
|
|
3605
|
+
this.primitiveChildren = new Map();
|
|
3606
|
+
this.objectChildren = new WeakMap();
|
|
3607
|
+
}
|
|
3608
|
+
child(part) {
|
|
3609
|
+
if ((typeof part === 'object' && part !== null) || typeof part === 'function') {
|
|
3610
|
+
let child = this.objectChildren.get(part);
|
|
3611
|
+
if (!child) {
|
|
3612
|
+
child = new SemanticKeyNode();
|
|
3613
|
+
this.objectChildren.set(part, child);
|
|
3614
|
+
}
|
|
3615
|
+
return child;
|
|
3616
|
+
}
|
|
3617
|
+
let child = this.primitiveChildren.get(part);
|
|
3618
|
+
if (!child) {
|
|
3619
|
+
child = new SemanticKeyNode();
|
|
3620
|
+
this.primitiveChildren.set(part, child);
|
|
3621
|
+
}
|
|
3622
|
+
return child;
|
|
3623
|
+
}
|
|
3624
|
+
}
|
|
3625
|
+
/**
|
|
3626
|
+
* Owns configured loader instances and their result promises. Constructor,
|
|
3627
|
+
* configuration, and URL are deliberately separate cache dimensions.
|
|
3628
|
+
*/
|
|
3629
|
+
class NgtLoaderCache {
|
|
3630
|
+
constructor() {
|
|
3631
|
+
this.constructors = new Map();
|
|
3632
|
+
this.configurationKeys = new SemanticKeyRegistry();
|
|
3633
|
+
this.requestKeys = new SemanticKeyRegistry();
|
|
3634
|
+
}
|
|
3635
|
+
configurationKey(extensions, key) {
|
|
3636
|
+
if (key !== undefined)
|
|
3637
|
+
return this.configurationKeys.resolve(key);
|
|
3638
|
+
return extensions ? {} : defaultConfigurationKey;
|
|
3639
|
+
}
|
|
3640
|
+
getOrCreate(LoaderConstructor, configurationKey, request, extensions, load) {
|
|
3641
|
+
const normalizedConfigurationKey = this.configurationKeys.resolve(configurationKey);
|
|
3642
|
+
const requestKey = this.requestKeys.resolve(request);
|
|
3643
|
+
const entry = this.getEntry(LoaderConstructor, normalizedConfigurationKey, extensions);
|
|
3644
|
+
const cachedPromise = entry.promises.get(requestKey);
|
|
3645
|
+
if (cachedPromise)
|
|
3646
|
+
return cachedPromise;
|
|
3647
|
+
let promise;
|
|
3648
|
+
promise = load(entry.loader).catch((error) => {
|
|
3649
|
+
this.evict(LoaderConstructor, normalizedConfigurationKey, entry, requestKey, promise);
|
|
3650
|
+
throw error;
|
|
3651
|
+
});
|
|
3652
|
+
entry.promises.set(requestKey, promise);
|
|
3653
|
+
return promise;
|
|
3654
|
+
}
|
|
3655
|
+
clear(urls) {
|
|
3656
|
+
const requestsToClear = Array.isArray(urls)
|
|
3657
|
+
? [this.requestKeys.resolve(urls), ...urls.map((url) => this.requestKeys.resolve(url))]
|
|
3658
|
+
: [this.requestKeys.resolve(urls)];
|
|
3659
|
+
for (const [LoaderConstructor, configurations] of this.constructors) {
|
|
3660
|
+
for (const [configurationKey, entry] of configurations) {
|
|
3661
|
+
for (const requestKey of requestsToClear)
|
|
3662
|
+
entry.promises.delete(requestKey);
|
|
3663
|
+
if (entry.promises.size === 0)
|
|
3664
|
+
configurations.delete(configurationKey);
|
|
3665
|
+
}
|
|
3666
|
+
if (configurations.size === 0)
|
|
3667
|
+
this.constructors.delete(LoaderConstructor);
|
|
3668
|
+
}
|
|
3669
|
+
}
|
|
3670
|
+
destroy() {
|
|
3671
|
+
this.constructors.clear();
|
|
3672
|
+
this.configurationKeys.clear();
|
|
3673
|
+
this.requestKeys.clear();
|
|
3674
|
+
}
|
|
3675
|
+
getConfigurations(LoaderConstructor) {
|
|
3676
|
+
let configurations = this.constructors.get(LoaderConstructor);
|
|
3677
|
+
if (!configurations) {
|
|
3678
|
+
configurations = new Map();
|
|
3679
|
+
this.constructors.set(LoaderConstructor, configurations);
|
|
3680
|
+
}
|
|
3681
|
+
return configurations;
|
|
3682
|
+
}
|
|
3683
|
+
getEntry(LoaderConstructor, configurationKey, extensions) {
|
|
3684
|
+
const configurations = this.getConfigurations(LoaderConstructor);
|
|
3685
|
+
let entry = configurations.get(configurationKey);
|
|
3686
|
+
if (entry)
|
|
3687
|
+
return entry;
|
|
3688
|
+
const loader = new LoaderConstructor();
|
|
3689
|
+
extensions?.(loader);
|
|
3690
|
+
entry = { loader, promises: new Map() };
|
|
3691
|
+
configurations.set(configurationKey, entry);
|
|
3692
|
+
return entry;
|
|
3693
|
+
}
|
|
3694
|
+
evict(LoaderConstructor, configurationKey, entry, requestKey, promise) {
|
|
3695
|
+
if (entry.promises.get(requestKey) !== promise)
|
|
3696
|
+
return;
|
|
3697
|
+
entry.promises.delete(requestKey);
|
|
3698
|
+
if (entry.promises.size > 0)
|
|
3699
|
+
return;
|
|
3700
|
+
const configurations = this.constructors.get(LoaderConstructor);
|
|
3701
|
+
if (configurations?.get(configurationKey) !== entry)
|
|
3702
|
+
return;
|
|
3703
|
+
configurations.delete(configurationKey);
|
|
3704
|
+
if (configurations.size === 0)
|
|
3705
|
+
this.constructors.delete(LoaderConstructor);
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
|
|
3709
|
+
const loaderCache$1 = new NgtLoaderCache();
|
|
3100
3710
|
function normalizeInputs$1(input) {
|
|
3101
3711
|
let urls = [];
|
|
3102
3712
|
if (Array.isArray(input)) {
|
|
@@ -3110,33 +3720,26 @@ function normalizeInputs$1(input) {
|
|
|
3110
3720
|
}
|
|
3111
3721
|
return urls.map((url) => (url.includes('undefined') || url.includes('null') || !url ? '' : url));
|
|
3112
3722
|
}
|
|
3113
|
-
function load(loaderConstructorFactory, inputs, { extensions, onLoad, onProgress, } = {}) {
|
|
3114
|
-
return () => {
|
|
3723
|
+
function load(loaderConstructorFactory, inputs, { extensions, cacheKey, onLoad, onProgress, } = {}) {
|
|
3724
|
+
return (consumerOnLoad = onLoad) => {
|
|
3115
3725
|
const urls = normalizeInputs$1(inputs());
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
loader = new (loaderConstructorFactory(urls))();
|
|
3119
|
-
memoizedLoaders$1.set(loaderConstructorFactory(urls), loader);
|
|
3120
|
-
}
|
|
3121
|
-
if (extensions)
|
|
3122
|
-
extensions(loader);
|
|
3726
|
+
const LoaderConstructor = loaderConstructorFactory(urls);
|
|
3727
|
+
const configurationKey = loaderCache$1.configurationKey(extensions, cacheKey?.());
|
|
3123
3728
|
return urls.map((url) => {
|
|
3124
3729
|
if (url === '')
|
|
3125
3730
|
return Promise.resolve(null);
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
}
|
|
3139
|
-
return cached$1.get(url);
|
|
3731
|
+
const promise = loaderCache$1.getOrCreate(LoaderConstructor, configurationKey, url, extensions, (loader) => new Promise((resolve, reject) => {
|
|
3732
|
+
loader.load(url, (data) => {
|
|
3733
|
+
if ('scene' in data) {
|
|
3734
|
+
Object.assign(data, makeObjectGraph(data['scene']));
|
|
3735
|
+
}
|
|
3736
|
+
resolve(data);
|
|
3737
|
+
}, onProgress, (error) => reject(new Error(`[NGT] Could not load ${url}: ${error?.message}`)));
|
|
3738
|
+
}));
|
|
3739
|
+
return promise.then((data) => {
|
|
3740
|
+
consumerOnLoad?.(data);
|
|
3741
|
+
return data;
|
|
3742
|
+
});
|
|
3140
3743
|
});
|
|
3141
3744
|
};
|
|
3142
3745
|
}
|
|
@@ -3144,18 +3747,30 @@ function load(loaderConstructorFactory, inputs, { extensions, onLoad, onProgress
|
|
|
3144
3747
|
* @deprecated Use loaderResource instead. Will be removed in v5.0.0
|
|
3145
3748
|
* @since v4.0.0~
|
|
3146
3749
|
*/
|
|
3147
|
-
function _injectLoader(loaderConstructorFactory, inputs, { extensions, onProgress, onLoad, injector, } = {}) {
|
|
3750
|
+
function _injectLoader(loaderConstructorFactory, inputs, { extensions, cacheKey, onProgress, onLoad, injector, } = {}) {
|
|
3148
3751
|
return assertInjector(_injectLoader, injector, () => {
|
|
3149
3752
|
const response = signal(null, ...(ngDevMode ? [{ debugName: "response" }] : /* istanbul ignore next */ []));
|
|
3150
3753
|
const cachedResultPromisesEffect = load(loaderConstructorFactory, inputs, {
|
|
3151
3754
|
extensions,
|
|
3755
|
+
cacheKey,
|
|
3152
3756
|
onProgress,
|
|
3153
|
-
onLoad: onLoad,
|
|
3154
3757
|
});
|
|
3155
|
-
effect(() => {
|
|
3758
|
+
effect((onCleanup) => {
|
|
3759
|
+
let active = true;
|
|
3760
|
+
onCleanup(() => {
|
|
3761
|
+
active = false;
|
|
3762
|
+
});
|
|
3156
3763
|
const originalUrls = inputs();
|
|
3157
|
-
const cachedResultPromises = cachedResultPromisesEffect()
|
|
3158
|
-
|
|
3764
|
+
const cachedResultPromises = cachedResultPromisesEffect((data) => {
|
|
3765
|
+
if (active)
|
|
3766
|
+
onLoad?.(data);
|
|
3767
|
+
});
|
|
3768
|
+
void Promise.all(cachedResultPromises)
|
|
3769
|
+
.then((results) => {
|
|
3770
|
+
// A cached request can resolve after the reactive input has moved on.
|
|
3771
|
+
// Keep the shared promise, but never publish a stale generation.
|
|
3772
|
+
if (!active)
|
|
3773
|
+
return;
|
|
3159
3774
|
response.update(() => {
|
|
3160
3775
|
if (Array.isArray(originalUrls))
|
|
3161
3776
|
return results;
|
|
@@ -3168,25 +3783,23 @@ function _injectLoader(loaderConstructorFactory, inputs, { extensions, onProgres
|
|
|
3168
3783
|
return result;
|
|
3169
3784
|
}, {});
|
|
3170
3785
|
});
|
|
3171
|
-
})
|
|
3786
|
+
})
|
|
3787
|
+
.catch(() => undefined);
|
|
3172
3788
|
});
|
|
3173
3789
|
return response.asReadonly();
|
|
3174
3790
|
});
|
|
3175
3791
|
}
|
|
3176
|
-
_injectLoader.preload = (loaderConstructorFactory, inputs, extensions, onLoad) => {
|
|
3177
|
-
const effects = load(loaderConstructorFactory, inputs, { extensions, onLoad })();
|
|
3792
|
+
_injectLoader.preload = (loaderConstructorFactory, inputs, extensions, onLoad, cacheKey) => {
|
|
3793
|
+
const effects = load(loaderConstructorFactory, inputs, { extensions, cacheKey, onLoad })();
|
|
3178
3794
|
if (effects) {
|
|
3179
|
-
void Promise.all(effects);
|
|
3795
|
+
void Promise.all(effects).catch(() => undefined);
|
|
3180
3796
|
}
|
|
3181
3797
|
};
|
|
3182
3798
|
_injectLoader.destroy = () => {
|
|
3183
|
-
|
|
3799
|
+
loaderCache$1.destroy();
|
|
3184
3800
|
};
|
|
3185
3801
|
_injectLoader.clear = (urls) => {
|
|
3186
|
-
|
|
3187
|
-
urlToClear.forEach((url) => {
|
|
3188
|
-
cached$1.delete(url);
|
|
3189
|
-
});
|
|
3802
|
+
loaderCache$1.clear(urls);
|
|
3190
3803
|
};
|
|
3191
3804
|
/**
|
|
3192
3805
|
* @deprecated Use loaderResource instead. Will be removed in v5.0.0
|
|
@@ -3214,38 +3827,26 @@ function normalizeInputs(input) {
|
|
|
3214
3827
|
}
|
|
3215
3828
|
return urls.map((url) => (url.includes('undefined') || url.includes('null') || !url ? '' : url));
|
|
3216
3829
|
}
|
|
3217
|
-
const
|
|
3218
|
-
|
|
3219
|
-
function getLoaderResourceParams(input, loaderConstructorFactory, extensions) {
|
|
3830
|
+
const loaderCache = new NgtLoaderCache();
|
|
3831
|
+
function getLoaderResourceParams(input, loaderConstructorFactory, extensions, cacheKey) {
|
|
3220
3832
|
const urls = input();
|
|
3221
3833
|
const LoaderConstructor = loaderConstructorFactory(urls);
|
|
3222
3834
|
const normalizedUrls = normalizeInputs(urls);
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
loader = new LoaderConstructor();
|
|
3226
|
-
memoizedLoaders.set(LoaderConstructor, loader);
|
|
3227
|
-
}
|
|
3228
|
-
if (extensions)
|
|
3229
|
-
extensions(loader);
|
|
3230
|
-
return { urls, normalizedUrls, loader };
|
|
3835
|
+
const configurationKey = loaderCache.configurationKey(extensions, cacheKey?.());
|
|
3836
|
+
return { urls, normalizedUrls, LoaderConstructor, configurationKey, extensions };
|
|
3231
3837
|
}
|
|
3232
3838
|
function getLoaderPromises(params, onProgress) {
|
|
3233
3839
|
return params.normalizedUrls.map((url) => {
|
|
3234
3840
|
if (url === '')
|
|
3235
3841
|
return Promise.resolve(null);
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
return cachedPromise;
|
|
3239
|
-
const promise = new Promise((res, rej) => {
|
|
3240
|
-
params.loader.load(url, (data) => {
|
|
3842
|
+
return loaderCache.getOrCreate(params.LoaderConstructor, params.configurationKey, url, params.extensions, (loader) => new Promise((res, rej) => {
|
|
3843
|
+
loader.load(url, (data) => {
|
|
3241
3844
|
if ('scene' in data) {
|
|
3242
3845
|
Object.assign(data, makeObjectGraph(data['scene']));
|
|
3243
3846
|
}
|
|
3244
3847
|
res(data);
|
|
3245
3848
|
}, onProgress, (error) => rej(new Error(`[NGT] Could not load ${url}: ${error?.message}`)));
|
|
3246
|
-
});
|
|
3247
|
-
cached.set(url, promise);
|
|
3248
|
-
return promise;
|
|
3849
|
+
}));
|
|
3249
3850
|
});
|
|
3250
3851
|
}
|
|
3251
3852
|
/**
|
|
@@ -3285,11 +3886,11 @@ function getLoaderPromises(params, onProgress) {
|
|
|
3285
3886
|
* }
|
|
3286
3887
|
* ```
|
|
3287
3888
|
*/
|
|
3288
|
-
function loaderResource(loaderConstructorFactory, input, { extensions, onLoad, onProgress, injector, } = {}) {
|
|
3889
|
+
function loaderResource(loaderConstructorFactory, input, { extensions, cacheKey, onLoad, onProgress, injector, } = {}) {
|
|
3289
3890
|
return assertInjector(loaderResource, injector, () => {
|
|
3290
3891
|
return resource({
|
|
3291
|
-
params: () => getLoaderResourceParams(input, loaderConstructorFactory, extensions),
|
|
3292
|
-
loader: async ({ params }) => {
|
|
3892
|
+
params: () => getLoaderResourceParams(input, loaderConstructorFactory, extensions, cacheKey),
|
|
3893
|
+
loader: async ({ params, abortSignal }) => {
|
|
3293
3894
|
// TODO: use the abortSignal when THREE.Loader supports it
|
|
3294
3895
|
const loadedResults = await Promise.all(getLoaderPromises(params, onProgress));
|
|
3295
3896
|
let results;
|
|
@@ -3307,25 +3908,22 @@ function loaderResource(loaderConstructorFactory, input, { extensions, onLoad, o
|
|
|
3307
3908
|
return result;
|
|
3308
3909
|
}, {});
|
|
3309
3910
|
}
|
|
3310
|
-
if (
|
|
3311
|
-
onLoad(results);
|
|
3911
|
+
if (!abortSignal.aborted)
|
|
3912
|
+
onLoad?.(results);
|
|
3312
3913
|
return results;
|
|
3313
3914
|
},
|
|
3314
3915
|
});
|
|
3315
3916
|
});
|
|
3316
3917
|
}
|
|
3317
|
-
loaderResource.preload = (loaderConstructor, inputs, extensions) => {
|
|
3318
|
-
const params = getLoaderResourceParams(() => inputs, () => loaderConstructor, extensions);
|
|
3319
|
-
void Promise.all(getLoaderPromises(params));
|
|
3918
|
+
loaderResource.preload = (loaderConstructor, inputs, extensions, cacheKey) => {
|
|
3919
|
+
const params = getLoaderResourceParams(() => inputs, () => loaderConstructor, extensions, cacheKey !== undefined ? () => cacheKey : undefined);
|
|
3920
|
+
void Promise.all(getLoaderPromises(params)).catch(() => undefined);
|
|
3320
3921
|
};
|
|
3321
3922
|
loaderResource.destroy = () => {
|
|
3322
|
-
|
|
3923
|
+
loaderCache.destroy();
|
|
3323
3924
|
};
|
|
3324
3925
|
loaderResource.clear = (urls) => {
|
|
3325
|
-
|
|
3326
|
-
urlToClear.forEach((url) => {
|
|
3327
|
-
cached.delete(url);
|
|
3328
|
-
});
|
|
3926
|
+
loaderCache.clear(urls);
|
|
3329
3927
|
};
|
|
3330
3928
|
|
|
3331
3929
|
const RGBA_REGEX = /rgba?\((\d+),\s*(\d+),\s*(\d+),?\s*(\d*\.?\d+)?\)/;
|
|
@@ -3649,7 +4247,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
3649
4247
|
* ```
|
|
3650
4248
|
*/
|
|
3651
4249
|
class NgtPortalContent {
|
|
3652
|
-
static ngTemplateContextGuard(_,
|
|
4250
|
+
static ngTemplateContextGuard(_, _ctx) {
|
|
3653
4251
|
return true;
|
|
3654
4252
|
}
|
|
3655
4253
|
constructor() {
|
|
@@ -3657,9 +4255,10 @@ class NgtPortalContent {
|
|
|
3657
4255
|
const { element } = inject(ViewContainerRef);
|
|
3658
4256
|
const commentNode = element.nativeElement;
|
|
3659
4257
|
const store = injectStore();
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
commentNode
|
|
4258
|
+
const domParent = host.nativeElement;
|
|
4259
|
+
const anchor = { kind: 'portal', store, domParent };
|
|
4260
|
+
setRendererAnchor(commentNode, anchor);
|
|
4261
|
+
setRendererAnchor(domParent, anchor);
|
|
3663
4262
|
}
|
|
3664
4263
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: NgtPortalContent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
3665
4264
|
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: NgtPortalContent, isStandalone: true, selector: "ng-template[portalContent]", ngImport: i0 }); }
|
|
@@ -3668,34 +4267,115 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
3668
4267
|
type: Directive,
|
|
3669
4268
|
args: [{ selector: 'ng-template[portalContent]' }]
|
|
3670
4269
|
}], ctorParameters: () => [] });
|
|
3671
|
-
function mergeState(previousRoot, store, container,
|
|
3672
|
-
//
|
|
3673
|
-
|
|
4270
|
+
function mergeState(previousRoot, store, previousState, container, runtime, restState, events, size) {
|
|
4271
|
+
// The portal id belongs to this store. Everything else starts from the
|
|
4272
|
+
// latest parent snapshot so removed overrides cannot leave stale values.
|
|
4273
|
+
const { id: _, ...inheritedState } = previousState;
|
|
3674
4274
|
const state = store.snapshot;
|
|
4275
|
+
capturePortalLocalState(previousState, state, runtime);
|
|
4276
|
+
const layeredState = { ...inheritedState, ...runtime.localState, ...restState };
|
|
4277
|
+
const camera = layeredState.camera;
|
|
4278
|
+
const mergedSize = { ...previousState.size, ...size };
|
|
3675
4279
|
let viewport = undefined;
|
|
3676
|
-
if (
|
|
3677
|
-
const camera = state.camera;
|
|
4280
|
+
if (camera && (camera !== previousState.camera || size)) {
|
|
3678
4281
|
// calculate the override viewport, if present
|
|
3679
|
-
viewport = previousState.viewport.getCurrentViewport(camera, new THREE.Vector3(),
|
|
3680
|
-
//
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
4282
|
+
viewport = previousState.viewport.getCurrentViewport(camera, new THREE.Vector3(), mergedSize);
|
|
4283
|
+
// A portal-local camera owns its projection until the portal explicitly
|
|
4284
|
+
// overrides the inherited size. Camera components may intentionally provide
|
|
4285
|
+
// custom orthographic bounds or a custom perspective aspect.
|
|
4286
|
+
if (size && camera !== previousState.camera)
|
|
4287
|
+
updateCamera(camera, mergedSize);
|
|
4288
|
+
}
|
|
4289
|
+
runtime.inputEvents = events;
|
|
4290
|
+
const mergedState = {
|
|
4291
|
+
// Inherited values are refreshed on every effect run. Current input
|
|
4292
|
+
// overrides are applied over that fresh snapshot, never over stale portal
|
|
4293
|
+
// state from a previous run.
|
|
4294
|
+
...layeredState,
|
|
4295
|
+
id: state.id,
|
|
3688
4296
|
// portals have their own scene, which forms the root, a raycaster and a pointer
|
|
3689
4297
|
scene: container,
|
|
3690
|
-
pointer,
|
|
3691
|
-
raycaster,
|
|
4298
|
+
pointer: runtime.pointer,
|
|
4299
|
+
raycaster: runtime.raycaster,
|
|
3692
4300
|
// their previous root is the layer before it
|
|
3693
4301
|
previousRoot,
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
4302
|
+
// The render loop and subscriptions are portal runtime state. Replacing
|
|
4303
|
+
// them with a new parent object would discard portal-local mutations.
|
|
4304
|
+
internal: state.internal,
|
|
4305
|
+
events: { ...previousState.events, ...runtime.localEvents, ...events },
|
|
4306
|
+
size: mergedSize,
|
|
4307
|
+
viewport: { ...previousState.viewport, ...viewport, ...restState.viewport },
|
|
4308
|
+
setEvents: runtime.setEvents,
|
|
4309
|
+
invalidate: runtime.invalidate,
|
|
4310
|
+
};
|
|
4311
|
+
runtime.lastMerged = mergedState;
|
|
4312
|
+
return mergedState;
|
|
4313
|
+
}
|
|
4314
|
+
function capturePortalLocalState(parent, current, runtime) {
|
|
4315
|
+
for (const key of ['camera', 'controls']) {
|
|
4316
|
+
const tracker = runtime.localStateTrackers[key];
|
|
4317
|
+
tracker.baselines.add(parent[key]);
|
|
4318
|
+
const previousValue = runtime.lastMerged?.[key];
|
|
4319
|
+
const currentValue = current[key];
|
|
4320
|
+
if (!runtime.lastMerged || Object.is(currentValue, previousValue))
|
|
4321
|
+
continue;
|
|
4322
|
+
if (tracker.baselines.has(currentValue)) {
|
|
4323
|
+
tracker.values.length = 0;
|
|
4324
|
+
delete runtime.localState[key];
|
|
4325
|
+
continue;
|
|
4326
|
+
}
|
|
4327
|
+
const restoredIndex = tracker.values.lastIndexOf(currentValue);
|
|
4328
|
+
if (restoredIndex >= 0)
|
|
4329
|
+
tracker.values.length = restoredIndex + 1;
|
|
4330
|
+
else
|
|
4331
|
+
tracker.values.push(currentValue);
|
|
4332
|
+
Object.assign(runtime.localState, { [key]: tracker.values.at(-1) });
|
|
4333
|
+
}
|
|
4334
|
+
}
|
|
4335
|
+
function movePortalChildren(store, previousContainer, nextContainer) {
|
|
4336
|
+
if (!previousContainer || previousContainer === nextContainer)
|
|
4337
|
+
return;
|
|
4338
|
+
const previousState = getInstanceState(previousContainer);
|
|
4339
|
+
if (!previousState)
|
|
4340
|
+
return;
|
|
4341
|
+
const children = [...previousState.objects(), ...previousState.nonObjects()];
|
|
4342
|
+
for (const child of children) {
|
|
4343
|
+
const childState = getInstanceState(child);
|
|
4344
|
+
if (childState?.store !== store || childState.parent() !== previousContainer)
|
|
4345
|
+
continue;
|
|
4346
|
+
removeThreeChild(child, previousContainer, false);
|
|
4347
|
+
attachThreeNodes(nextContainer, child, undefined, store);
|
|
4348
|
+
}
|
|
4349
|
+
}
|
|
4350
|
+
const portalRuntimes = new WeakMap();
|
|
4351
|
+
const portalContainerOwnership = new WeakMap();
|
|
4352
|
+
function claimPortalContainer(container, store, fallback) {
|
|
4353
|
+
const instanceState = getInstanceState(container);
|
|
4354
|
+
if (!instanceState)
|
|
4355
|
+
return () => undefined;
|
|
4356
|
+
let ownership = portalContainerOwnership.get(container);
|
|
4357
|
+
if (!ownership) {
|
|
4358
|
+
ownership = { baseline: instanceState.store ?? fallback, claims: [] };
|
|
4359
|
+
portalContainerOwnership.set(container, ownership);
|
|
4360
|
+
}
|
|
4361
|
+
const token = Symbol('portal-container');
|
|
4362
|
+
ownership.claims.push({ token, store });
|
|
4363
|
+
instanceState.store = store;
|
|
4364
|
+
let active = true;
|
|
4365
|
+
return () => {
|
|
4366
|
+
if (!active)
|
|
4367
|
+
return;
|
|
4368
|
+
active = false;
|
|
4369
|
+
const current = portalContainerOwnership.get(container);
|
|
4370
|
+
if (!current)
|
|
4371
|
+
return;
|
|
4372
|
+
const index = current.claims.findIndex((claim) => claim.token === token);
|
|
4373
|
+
if (index >= 0)
|
|
4374
|
+
current.claims.splice(index, 1);
|
|
4375
|
+
const latest = current.claims.at(-1);
|
|
4376
|
+
instanceState.store = latest?.store ?? current.baseline;
|
|
4377
|
+
if (!latest)
|
|
4378
|
+
portalContainerOwnership.delete(container);
|
|
3699
4379
|
};
|
|
3700
4380
|
}
|
|
3701
4381
|
/**
|
|
@@ -3719,6 +4399,16 @@ function mergeState(previousRoot, store, container, pointer, raycaster, events,
|
|
|
3719
4399
|
* ```
|
|
3720
4400
|
*/
|
|
3721
4401
|
class NgtPortalImpl {
|
|
4402
|
+
/** @internal Subscribe to invalidations originating in this portal layer. */
|
|
4403
|
+
onInvalidate(listener) {
|
|
4404
|
+
const runtime = portalRuntimes.get(this.portalStore);
|
|
4405
|
+
if (!runtime)
|
|
4406
|
+
throw new Error('[NGT] Portal store runtime was not initialized');
|
|
4407
|
+
runtime.invalidationListeners.add(listener);
|
|
4408
|
+
return () => {
|
|
4409
|
+
runtime.invalidationListeners.delete(listener);
|
|
4410
|
+
};
|
|
4411
|
+
}
|
|
3722
4412
|
constructor() {
|
|
3723
4413
|
this.container = input.required(...(ngDevMode ? [{ debugName: "container" }] : /* istanbul ignore next */ []));
|
|
3724
4414
|
this.state = input({}, ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
|
|
@@ -3727,6 +4417,7 @@ class NgtPortalImpl {
|
|
|
3727
4417
|
this.previousStore = injectStore({ skipSelf: true });
|
|
3728
4418
|
this.portalStore = injectStore();
|
|
3729
4419
|
this.injector = inject(Injector);
|
|
4420
|
+
this.document = inject(DOCUMENT);
|
|
3730
4421
|
this.size = pick(this.state, 'size');
|
|
3731
4422
|
this.events = pick(this.state, 'events');
|
|
3732
4423
|
this.restState = omit(this.state, ['size', 'events']);
|
|
@@ -3734,21 +4425,42 @@ class NgtPortalImpl {
|
|
|
3734
4425
|
this.portalRendered = this.portalContentRendered.asReadonly();
|
|
3735
4426
|
extend({ Group });
|
|
3736
4427
|
effect(() => {
|
|
3737
|
-
|
|
4428
|
+
// A makeDefault camera/controls update writes directly to the portal
|
|
4429
|
+
// store. Track those supported local overrides without tracking the
|
|
4430
|
+
// whole state object that this effect itself merges.
|
|
4431
|
+
this.portalStore.camera();
|
|
4432
|
+
this.portalStore.controls();
|
|
4433
|
+
const [inputContainer, anchor, content, previousState, size, events, restState] = [
|
|
3738
4434
|
this.container(),
|
|
3739
4435
|
this.anchorRef(),
|
|
3740
4436
|
this.contentRef(),
|
|
3741
4437
|
this.previousStore(),
|
|
4438
|
+
this.size(),
|
|
4439
|
+
this.events(),
|
|
4440
|
+
this.restState(),
|
|
3742
4441
|
];
|
|
3743
|
-
const
|
|
3744
|
-
if (!
|
|
3745
|
-
|
|
4442
|
+
const runtime = portalRuntimes.get(this.portalStore);
|
|
4443
|
+
if (!runtime)
|
|
4444
|
+
throw new Error('[NGT] Portal store runtime was not initialized');
|
|
4445
|
+
const instanceContainer = is.instance(inputContainer)
|
|
4446
|
+
? inputContainer
|
|
4447
|
+
: prepare(inputContainer, 'ngt-portal', { store: this.previousStore });
|
|
4448
|
+
const container = isRendererNodeType(instanceContainer, 'three')
|
|
4449
|
+
? instanceContainer
|
|
4450
|
+
: createRendererNode('three', instanceContainer, this.document);
|
|
4451
|
+
const portalAnchor = getRendererAnchor(anchor.element.nativeElement);
|
|
4452
|
+
if (portalAnchor?.kind === 'portal' && portalAnchor.domParent) {
|
|
4453
|
+
portalAnchor.domParent.__ngt_renderer__[3 /* NgtRendererClassId.portalContainer */] = container;
|
|
3746
4454
|
}
|
|
3747
4455
|
const instanceState = getInstanceState(container);
|
|
3748
|
-
if (instanceState &&
|
|
3749
|
-
|
|
4456
|
+
if (instanceState && runtime.container !== container) {
|
|
4457
|
+
const nextRelease = claimPortalContainer(container, this.portalStore, this.previousStore);
|
|
4458
|
+
movePortalChildren(this.portalStore, runtime.container, container);
|
|
4459
|
+
runtime.containerClaim?.release();
|
|
4460
|
+
runtime.container = container;
|
|
4461
|
+
runtime.containerClaim = { container, release: nextRelease };
|
|
3750
4462
|
}
|
|
3751
|
-
this.portalStore.update(
|
|
4463
|
+
this.portalStore.update(mergeState(this.previousStore, this.portalStore, previousState, container, runtime, restState, events, size));
|
|
3752
4464
|
if (this.portalViewRef) {
|
|
3753
4465
|
this.portalViewRef.detectChanges();
|
|
3754
4466
|
return;
|
|
@@ -3758,6 +4470,12 @@ class NgtPortalImpl {
|
|
|
3758
4470
|
this.portalViewRef.detectChanges();
|
|
3759
4471
|
this.portalContentRendered.set(true);
|
|
3760
4472
|
});
|
|
4473
|
+
inject(DestroyRef).onDestroy(() => {
|
|
4474
|
+
const runtime = portalRuntimes.get(this.portalStore);
|
|
4475
|
+
runtime?.containerClaim?.release();
|
|
4476
|
+
runtime?.invalidationListeners.clear();
|
|
4477
|
+
portalRuntimes.delete(this.portalStore);
|
|
4478
|
+
});
|
|
3761
4479
|
}
|
|
3762
4480
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: NgtPortalImpl, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3763
4481
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: NgtPortalImpl, isStandalone: true, selector: "ngt-portal", inputs: { container: { classPropertyName: "container", publicName: "container", isSignal: true, isRequired: true, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
|
|
@@ -3775,7 +4493,32 @@ class NgtPortalImpl {
|
|
|
3775
4493
|
pointer,
|
|
3776
4494
|
raycaster,
|
|
3777
4495
|
});
|
|
3778
|
-
|
|
4496
|
+
const runtime = {
|
|
4497
|
+
pointer,
|
|
4498
|
+
raycaster,
|
|
4499
|
+
localEvents: {},
|
|
4500
|
+
invalidate: () => undefined,
|
|
4501
|
+
invalidationListeners: new Set(),
|
|
4502
|
+
localState: {},
|
|
4503
|
+
localStateTrackers: {
|
|
4504
|
+
camera: { baselines: new Set(), values: [] },
|
|
4505
|
+
controls: { baselines: new Set(), values: [] },
|
|
4506
|
+
},
|
|
4507
|
+
setEvents: () => undefined,
|
|
4508
|
+
};
|
|
4509
|
+
runtime.setEvents = (events) => {
|
|
4510
|
+
Object.assign(runtime.localEvents, events);
|
|
4511
|
+
store.update((state) => ({
|
|
4512
|
+
events: { ...state.events, ...events, ...runtime.inputEvents },
|
|
4513
|
+
}));
|
|
4514
|
+
};
|
|
4515
|
+
runtime.invalidate = (frames) => {
|
|
4516
|
+
for (const listener of runtime.invalidationListeners)
|
|
4517
|
+
listener();
|
|
4518
|
+
previousStore.snapshot.invalidate(frames);
|
|
4519
|
+
};
|
|
4520
|
+
portalRuntimes.set(store, runtime);
|
|
4521
|
+
store.update(mergeState(previousStore, store, previousStore.snapshot, null, runtime, {}));
|
|
3779
4522
|
return store;
|
|
3780
4523
|
},
|
|
3781
4524
|
deps: [[new SkipSelf(), NGT_STORE]],
|
|
@@ -3814,7 +4557,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
3814
4557
|
pointer,
|
|
3815
4558
|
raycaster,
|
|
3816
4559
|
});
|
|
3817
|
-
|
|
4560
|
+
const runtime = {
|
|
4561
|
+
pointer,
|
|
4562
|
+
raycaster,
|
|
4563
|
+
localEvents: {},
|
|
4564
|
+
invalidate: () => undefined,
|
|
4565
|
+
invalidationListeners: new Set(),
|
|
4566
|
+
localState: {},
|
|
4567
|
+
localStateTrackers: {
|
|
4568
|
+
camera: { baselines: new Set(), values: [] },
|
|
4569
|
+
controls: { baselines: new Set(), values: [] },
|
|
4570
|
+
},
|
|
4571
|
+
setEvents: () => undefined,
|
|
4572
|
+
};
|
|
4573
|
+
runtime.setEvents = (events) => {
|
|
4574
|
+
Object.assign(runtime.localEvents, events);
|
|
4575
|
+
store.update((state) => ({
|
|
4576
|
+
events: { ...state.events, ...events, ...runtime.inputEvents },
|
|
4577
|
+
}));
|
|
4578
|
+
};
|
|
4579
|
+
runtime.invalidate = (frames) => {
|
|
4580
|
+
for (const listener of runtime.invalidationListeners)
|
|
4581
|
+
listener();
|
|
4582
|
+
previousStore.snapshot.invalidate(frames);
|
|
4583
|
+
};
|
|
4584
|
+
portalRuntimes.set(store, runtime);
|
|
4585
|
+
store.update(mergeState(previousStore, store, previousStore.snapshot, null, runtime, {}));
|
|
3818
4586
|
return store;
|
|
3819
4587
|
},
|
|
3820
4588
|
deps: [[new SkipSelf(), NGT_STORE]],
|
|
@@ -3836,6 +4604,95 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
3836
4604
|
const NgtPortal = [NgtPortalImpl, NgtPortalContent];
|
|
3837
4605
|
|
|
3838
4606
|
const shallowLoose = { objects: 'shallow', strict: false };
|
|
4607
|
+
const rootLifecycles = new WeakMap();
|
|
4608
|
+
function cloneConfigurationValue(value) {
|
|
4609
|
+
if (Array.isArray(value))
|
|
4610
|
+
return value.slice();
|
|
4611
|
+
if (value && typeof value === 'object' && 'clone' in value && typeof value.clone === 'function') {
|
|
4612
|
+
return value.clone();
|
|
4613
|
+
}
|
|
4614
|
+
return value;
|
|
4615
|
+
}
|
|
4616
|
+
function snapshotCameraOptions(camera) {
|
|
4617
|
+
if (!camera || is.three(camera, 'isCamera'))
|
|
4618
|
+
return camera;
|
|
4619
|
+
const options = camera;
|
|
4620
|
+
return Object.fromEntries(Object.entries(options).map(([key, value]) => [key, cloneConfigurationValue(value)]));
|
|
4621
|
+
}
|
|
4622
|
+
function configurationValuesEqual(previous, next) {
|
|
4623
|
+
if (Object.is(previous, next))
|
|
4624
|
+
return true;
|
|
4625
|
+
if (Array.isArray(previous) && Array.isArray(next)) {
|
|
4626
|
+
return previous.length === next.length && previous.every((value, index) => Object.is(value, next[index]));
|
|
4627
|
+
}
|
|
4628
|
+
if (previous &&
|
|
4629
|
+
next &&
|
|
4630
|
+
typeof previous === 'object' &&
|
|
4631
|
+
typeof next === 'object' &&
|
|
4632
|
+
'equals' in previous &&
|
|
4633
|
+
typeof previous.equals === 'function') {
|
|
4634
|
+
return previous.equals(next);
|
|
4635
|
+
}
|
|
4636
|
+
return false;
|
|
4637
|
+
}
|
|
4638
|
+
function cameraOptionsEqual(previous, next) {
|
|
4639
|
+
if (Object.is(previous, next))
|
|
4640
|
+
return true;
|
|
4641
|
+
if (!previous || !next)
|
|
4642
|
+
return false;
|
|
4643
|
+
if (is.three(previous, 'isCamera') || is.three(next, 'isCamera'))
|
|
4644
|
+
return false;
|
|
4645
|
+
const previousKeys = Object.keys(previous);
|
|
4646
|
+
const nextKeys = Object.keys(next);
|
|
4647
|
+
const previousRecord = previous;
|
|
4648
|
+
const nextRecord = next;
|
|
4649
|
+
return (previousKeys.length === nextKeys.length &&
|
|
4650
|
+
previousKeys.every((key) => Object.prototype.hasOwnProperty.call(nextRecord, key) &&
|
|
4651
|
+
configurationValuesEqual(previousRecord[key], nextRecord[key])));
|
|
4652
|
+
}
|
|
4653
|
+
function cameraConfigurationsEqual(previous, next) {
|
|
4654
|
+
return (!!previous &&
|
|
4655
|
+
previous.orthographic === next.orthographic &&
|
|
4656
|
+
cameraOptionsEqual(previous.camera, next.camera) &&
|
|
4657
|
+
configurationValuesEqual(previous.lookAt, next.lookAt));
|
|
4658
|
+
}
|
|
4659
|
+
function createManagedCamera(configuration, store, size = store.snapshot.size) {
|
|
4660
|
+
const { camera: cameraOptions, lookAt, orthographic } = configuration;
|
|
4661
|
+
const isCamera = is.three(cameraOptions, 'isCamera');
|
|
4662
|
+
let camera = isCamera ? cameraOptions : makeCameraInstance(orthographic, size);
|
|
4663
|
+
if (!isCamera) {
|
|
4664
|
+
camera.position.z = 5;
|
|
4665
|
+
if (cameraOptions) {
|
|
4666
|
+
applyProps(camera, cameraOptions);
|
|
4667
|
+
if ('aspect' in cameraOptions ||
|
|
4668
|
+
'left' in cameraOptions ||
|
|
4669
|
+
'right' in cameraOptions ||
|
|
4670
|
+
'top' in cameraOptions ||
|
|
4671
|
+
'bottom' in cameraOptions) {
|
|
4672
|
+
Object.assign(camera, { manual: true });
|
|
4673
|
+
camera.updateProjectionMatrix();
|
|
4674
|
+
}
|
|
4675
|
+
}
|
|
4676
|
+
if (!cameraOptions?.rotation && !cameraOptions?.quaternion) {
|
|
4677
|
+
if (Array.isArray(lookAt))
|
|
4678
|
+
camera.lookAt(lookAt[0], lookAt[1], lookAt[2]);
|
|
4679
|
+
else if (typeof lookAt === 'number')
|
|
4680
|
+
camera.lookAt(lookAt, lookAt, lookAt);
|
|
4681
|
+
else if (lookAt?.isVector3)
|
|
4682
|
+
camera.lookAt(lookAt);
|
|
4683
|
+
else
|
|
4684
|
+
camera.lookAt(0, 0, 0);
|
|
4685
|
+
}
|
|
4686
|
+
camera.updateProjectionMatrix?.();
|
|
4687
|
+
}
|
|
4688
|
+
if (!is.instance(camera))
|
|
4689
|
+
camera = prepare(camera, '', { store });
|
|
4690
|
+
return camera;
|
|
4691
|
+
}
|
|
4692
|
+
function cancelPerformanceRegression(store) {
|
|
4693
|
+
const regress = store.snapshot.performance.regress;
|
|
4694
|
+
regress.cancel?.();
|
|
4695
|
+
}
|
|
3839
4696
|
/**
|
|
3840
4697
|
* Creates a canvas root initializer function.
|
|
3841
4698
|
*
|
|
@@ -3857,10 +4714,12 @@ function canvasRootInitializer(injector) {
|
|
|
3857
4714
|
return assertInjector(canvasRootInitializer, injector, () => {
|
|
3858
4715
|
const injectedStore = injectStore();
|
|
3859
4716
|
const loop = injectLoop();
|
|
4717
|
+
const rootInjector = inject(Injector);
|
|
3860
4718
|
return (canvas) => {
|
|
3861
4719
|
const exist = roots.has(canvas);
|
|
3862
4720
|
let store = roots.get(canvas);
|
|
3863
|
-
|
|
4721
|
+
const previousLifecycle = rootLifecycles.get(canvas);
|
|
4722
|
+
if (store && !previousLifecycle?.teardown) {
|
|
3864
4723
|
console.warn('[NGT] Same canvas root is being created twice');
|
|
3865
4724
|
}
|
|
3866
4725
|
store ||= injectedStore;
|
|
@@ -3870,33 +4729,95 @@ function canvasRootInitializer(injector) {
|
|
|
3870
4729
|
if (!exist) {
|
|
3871
4730
|
roots.set(canvas, store);
|
|
3872
4731
|
}
|
|
4732
|
+
if (previousLifecycle?.teardown) {
|
|
4733
|
+
clearTimeout(previousLifecycle.teardown);
|
|
4734
|
+
previousLifecycle.teardown = undefined;
|
|
4735
|
+
const performance = store.snapshot.performance;
|
|
4736
|
+
if (performance.current !== performance.max) {
|
|
4737
|
+
store.update((state) => ({
|
|
4738
|
+
performance: { ...state.performance, current: state.performance.max },
|
|
4739
|
+
}));
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4742
|
+
const owner = {};
|
|
4743
|
+
const lifecycle = previousLifecycle?.store === store ? previousLifecycle : { store, owner };
|
|
4744
|
+
lifecycle.owner = owner;
|
|
4745
|
+
rootLifecycles.set(canvas, lifecycle);
|
|
4746
|
+
lifecycle.cameraWatcher ??= effect(() => {
|
|
4747
|
+
const activeCamera = store.camera();
|
|
4748
|
+
const desired = lifecycle.cameraConfiguration;
|
|
4749
|
+
if (!desired ||
|
|
4750
|
+
!lifecycle.managedCamera ||
|
|
4751
|
+
activeCamera !== lifecycle.managedCamera ||
|
|
4752
|
+
cameraConfigurationsEqual(lifecycle.appliedCameraConfiguration, desired))
|
|
4753
|
+
return;
|
|
4754
|
+
const camera = createManagedCamera(desired, store);
|
|
4755
|
+
lifecycle.managedCamera = camera;
|
|
4756
|
+
lifecycle.appliedCameraConfiguration = desired;
|
|
4757
|
+
const raycaster = store.snapshot.raycaster;
|
|
4758
|
+
if (raycaster)
|
|
4759
|
+
raycaster.camera = camera;
|
|
4760
|
+
store.update({ camera });
|
|
4761
|
+
}, { injector: rootInjector });
|
|
3873
4762
|
let isConfigured = false;
|
|
3874
|
-
let
|
|
4763
|
+
let destroyRequested = false;
|
|
3875
4764
|
return {
|
|
3876
|
-
isConfigured
|
|
4765
|
+
get isConfigured() {
|
|
4766
|
+
return isConfigured;
|
|
4767
|
+
},
|
|
3877
4768
|
destroy: (timeout = 500) => {
|
|
4769
|
+
if (destroyRequested)
|
|
4770
|
+
return;
|
|
4771
|
+
destroyRequested = true;
|
|
3878
4772
|
const root = roots.get(canvas);
|
|
3879
|
-
if (root
|
|
4773
|
+
if (root &&
|
|
4774
|
+
root === store &&
|
|
4775
|
+
rootLifecycles.get(canvas) === lifecycle &&
|
|
4776
|
+
lifecycle.owner === owner) {
|
|
3880
4777
|
root.update((state) => ({ internal: { ...state.internal, active: false } }));
|
|
3881
|
-
|
|
4778
|
+
cancelPerformanceRegression(root);
|
|
4779
|
+
const teardown = setTimeout(() => {
|
|
4780
|
+
if (roots.get(canvas) !== root ||
|
|
4781
|
+
rootLifecycles.get(canvas) !== lifecycle ||
|
|
4782
|
+
lifecycle.owner !== owner ||
|
|
4783
|
+
lifecycle.teardown !== teardown)
|
|
4784
|
+
return;
|
|
4785
|
+
lifecycle.teardown = undefined;
|
|
3882
4786
|
try {
|
|
3883
4787
|
const state = root.snapshot;
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
4788
|
+
const failures = [];
|
|
4789
|
+
const attempt = (cleanup) => {
|
|
4790
|
+
try {
|
|
4791
|
+
cleanup();
|
|
4792
|
+
}
|
|
4793
|
+
catch (error) {
|
|
4794
|
+
failures.push(error);
|
|
4795
|
+
}
|
|
4796
|
+
};
|
|
4797
|
+
attempt(() => state.events.disconnect?.());
|
|
4798
|
+
attempt(() => state.gl?.renderLists?.dispose?.());
|
|
4799
|
+
attempt(() => state.gl?.dispose?.());
|
|
4800
|
+
attempt(() => state.gl?.forceContextLoss?.());
|
|
4801
|
+
attempt(() => state.xr?.disconnect?.());
|
|
4802
|
+
attempt(() => dispose(state.scene));
|
|
4803
|
+
attempt(() => lifecycle.cameraWatcher?.destroy());
|
|
4804
|
+
if (failures.length) {
|
|
4805
|
+
console.error('[NGT] Errors while destroying Canvas Root', failures);
|
|
4806
|
+
}
|
|
3892
4807
|
}
|
|
3893
|
-
|
|
3894
|
-
|
|
4808
|
+
finally {
|
|
4809
|
+
if (roots.get(canvas) === root)
|
|
4810
|
+
roots.delete(canvas);
|
|
4811
|
+
if (rootLifecycles.get(canvas) === lifecycle)
|
|
4812
|
+
rootLifecycles.delete(canvas);
|
|
3895
4813
|
}
|
|
3896
4814
|
}, timeout);
|
|
4815
|
+
lifecycle.teardown = teardown;
|
|
3897
4816
|
}
|
|
3898
4817
|
},
|
|
3899
4818
|
configure: (inputs) => {
|
|
4819
|
+
if (destroyRequested || rootLifecycles.get(canvas) !== lifecycle || lifecycle.owner !== owner)
|
|
4820
|
+
return;
|
|
3900
4821
|
const { shadows = false, linear = false, flat = false, legacy = false, orthographic = false, frameloop = 'always', dpr = [1, 2], gl: glOptions, size: sizeOptions, camera: cameraOptions, raycaster: raycasterOptions, scene: sceneOptions, events, lookAt, performance, } = inputs;
|
|
3901
4822
|
const state = store.snapshot;
|
|
3902
4823
|
const stateToUpdate = {};
|
|
@@ -3915,44 +4836,20 @@ function canvasRootInitializer(injector) {
|
|
|
3915
4836
|
if (!is.equ(params, raycaster.params, shallowLoose)) {
|
|
3916
4837
|
applyProps(raycaster, { params: { ...raycaster.params, ...(params || {}) } });
|
|
3917
4838
|
}
|
|
3918
|
-
|
|
4839
|
+
const cameraConfiguration = {
|
|
4840
|
+
camera: snapshotCameraOptions(cameraOptions),
|
|
4841
|
+
lookAt: cloneConfigurationValue(lookAt),
|
|
4842
|
+
orthographic,
|
|
4843
|
+
};
|
|
4844
|
+
lifecycle.cameraConfiguration = cameraConfiguration;
|
|
4845
|
+
// Create the root-managed camera, but don't overwrite a camera installed directly in the store.
|
|
3919
4846
|
if (!state.camera ||
|
|
3920
|
-
(state.camera ===
|
|
3921
|
-
|
|
3922
|
-
const
|
|
3923
|
-
let camera = isCamera
|
|
3924
|
-
? cameraOptions
|
|
3925
|
-
: makeCameraInstance(orthographic, sizeOptions ?? state.size);
|
|
3926
|
-
if (!isCamera) {
|
|
3927
|
-
camera.position.z = 5;
|
|
3928
|
-
if (cameraOptions) {
|
|
3929
|
-
applyProps(camera, cameraOptions);
|
|
3930
|
-
if ('aspect' in cameraOptions ||
|
|
3931
|
-
'left' in cameraOptions ||
|
|
3932
|
-
'right' in cameraOptions ||
|
|
3933
|
-
'top' in cameraOptions ||
|
|
3934
|
-
'bottom' in cameraOptions) {
|
|
3935
|
-
Object.assign(camera, { manual: true });
|
|
3936
|
-
camera?.updateProjectionMatrix();
|
|
3937
|
-
}
|
|
3938
|
-
}
|
|
3939
|
-
// always look at center or passed-in lookAt by default
|
|
3940
|
-
if (!state.camera && !cameraOptions?.rotation && !cameraOptions?.quaternion) {
|
|
3941
|
-
if (Array.isArray(lookAt))
|
|
3942
|
-
camera.lookAt(lookAt[0], lookAt[1], lookAt[2]);
|
|
3943
|
-
else if (typeof lookAt === 'number')
|
|
3944
|
-
camera.lookAt(lookAt, lookAt, lookAt);
|
|
3945
|
-
else if (lookAt?.isVector3)
|
|
3946
|
-
camera.lookAt(lookAt);
|
|
3947
|
-
else
|
|
3948
|
-
camera.lookAt(0, 0, 0);
|
|
3949
|
-
}
|
|
3950
|
-
// update projection matrix after applyprops
|
|
3951
|
-
camera.updateProjectionMatrix?.();
|
|
3952
|
-
}
|
|
3953
|
-
if (!is.instance(camera))
|
|
3954
|
-
camera = prepare(camera, '', { store });
|
|
4847
|
+
(state.camera === lifecycle.managedCamera &&
|
|
4848
|
+
!cameraConfigurationsEqual(lifecycle.appliedCameraConfiguration, cameraConfiguration))) {
|
|
4849
|
+
const camera = createManagedCamera(cameraConfiguration, store, sizeOptions ?? state.size);
|
|
3955
4850
|
stateToUpdate.camera = camera;
|
|
4851
|
+
lifecycle.managedCamera = camera;
|
|
4852
|
+
lifecycle.appliedCameraConfiguration = cameraConfiguration;
|
|
3956
4853
|
// Configure raycaster
|
|
3957
4854
|
// https://github.com/pmndrs/react-xr/issues/300
|
|
3958
4855
|
raycaster.camera = camera;
|
|
@@ -4321,15 +5218,24 @@ function elementEvents(target, events, { injector } = {}) {
|
|
|
4321
5218
|
const targetObj = resolveRef(target());
|
|
4322
5219
|
if (!targetObj || !is.instance(targetObj))
|
|
4323
5220
|
return;
|
|
5221
|
+
const currentCleanUps = [];
|
|
4324
5222
|
Object.keys(events).forEach((eventName) => {
|
|
4325
|
-
|
|
5223
|
+
const cleanUp = renderer.listen(targetObj, eventName, events[eventName]);
|
|
5224
|
+
currentCleanUps.push(cleanUp);
|
|
5225
|
+
cleanUps.push(cleanUp);
|
|
4326
5226
|
});
|
|
4327
5227
|
onCleanup(() => {
|
|
4328
|
-
|
|
5228
|
+
for (const cleanUp of currentCleanUps) {
|
|
5229
|
+
cleanUp();
|
|
5230
|
+
const index = cleanUps.indexOf(cleanUp);
|
|
5231
|
+
if (index >= 0)
|
|
5232
|
+
cleanUps.splice(index, 1);
|
|
5233
|
+
}
|
|
4329
5234
|
});
|
|
4330
5235
|
});
|
|
4331
5236
|
inject(DestroyRef).onDestroy(() => {
|
|
4332
|
-
|
|
5237
|
+
for (const cleanUp of cleanUps.splice(0))
|
|
5238
|
+
cleanUp();
|
|
4333
5239
|
});
|
|
4334
5240
|
return cleanUps;
|
|
4335
5241
|
});
|
|
@@ -4448,15 +5354,24 @@ function objectEvents(target, events, { injector } = {}) {
|
|
|
4448
5354
|
const targetObj = resolveRef(target());
|
|
4449
5355
|
if (!targetObj || !is.instance(targetObj))
|
|
4450
5356
|
return;
|
|
5357
|
+
const currentCleanUps = [];
|
|
4451
5358
|
Object.entries(events).forEach(([eventName, eventHandler]) => {
|
|
4452
|
-
|
|
5359
|
+
const cleanUp = renderer.listen(targetObj, eventName, eventHandler);
|
|
5360
|
+
currentCleanUps.push(cleanUp);
|
|
5361
|
+
cleanUps.push(cleanUp);
|
|
4453
5362
|
});
|
|
4454
5363
|
onCleanup(() => {
|
|
4455
|
-
|
|
5364
|
+
for (const cleanUp of currentCleanUps) {
|
|
5365
|
+
cleanUp();
|
|
5366
|
+
const index = cleanUps.indexOf(cleanUp);
|
|
5367
|
+
if (index >= 0)
|
|
5368
|
+
cleanUps.splice(index, 1);
|
|
5369
|
+
}
|
|
4456
5370
|
});
|
|
4457
5371
|
});
|
|
4458
5372
|
inject(DestroyRef).onDestroy(() => {
|
|
4459
|
-
|
|
5373
|
+
for (const cleanUp of cleanUps.splice(0))
|
|
5374
|
+
cleanUp();
|
|
4460
5375
|
});
|
|
4461
5376
|
return cleanUps;
|
|
4462
5377
|
});
|
|
@@ -4488,5 +5403,5 @@ function hasListener(...emitterRefs) {
|
|
|
4488
5403
|
* Generated bundle index. Do not edit.
|
|
4489
5404
|
*/
|
|
4490
5405
|
|
|
4491
|
-
export { NGT_APPLY_PROPS,
|
|
5406
|
+
export { NGT_APPLY_PROPS, NGT_CATALOGUE, NGT_DOM_PARENT_FLAG, NGT_GET_NODE_ATTRIBUTE_FLAG, NGT_HTML_FLAG, NGT_LOOP, NGT_RENDERER_CONTEXT_FLAG, NGT_RENDERER_NODE_FLAG, NGT_RENDERER_OPTIONS, NGT_STORE, NgtArgs, NgtElementEvents, NgtHTML, NgtHexify, NgtObjectEvents, NgtParent, NgtPortal, NgtPortalAutoRender, NgtPortalContent, NgtPortalImpl, NgtRenderer2, NgtRendererFactory2, NgtRoutedScene, NgtSelect, NgtSelection, NgtSelectionApi, THREE_NATIVE_EVENTS, addAfterEffect, addEffect, addTail, applyProps, attach, beforeRender, canvasRootInitializer, checkNeedsUpdate, checkUpdate, createAttachFunction, createEvents, detach, dispose, elementEvents, extend, flushAncestorNotifications, flushGlobalEffects, getEmitter, getInstanceState, getLocalState, hasListener, injectBeforeRender, injectCatalogue, injectLoader, injectLoop, injectObjectEvents, injectStore, invalidateInstance, is, loaderResource, makeCameraInstance, makeDpr, makeId, makeObjectGraph, makeRendererInstance, merge, objectEvents, omit, pick, prepare, provideHTMLDomElement, remove, removeInteractivity, resolveInstanceKey, resolveRef, roots, setRendererAnchor, signalState, storeFactory, toDeepSignal, updateCamera, uuidv4Fallback, vector2, vector3, vector4 };
|
|
4492
5407
|
//# sourceMappingURL=angular-three.mjs.map
|