angular-three 4.2.2 → 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 +1577 -652
- 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 +120 -69
|
@@ -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 });
|
|
@@ -614,6 +711,15 @@ function updateCamera(camera, size) {
|
|
|
614
711
|
}
|
|
615
712
|
}
|
|
616
713
|
|
|
714
|
+
/** RFC 4122 v4 UUID — safe in non-secure contexts (e.g. http:// on LAN). */
|
|
715
|
+
function uuidv4Fallback() {
|
|
716
|
+
const bytes = new Uint8Array(16);
|
|
717
|
+
crypto.getRandomValues(bytes);
|
|
718
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
719
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
720
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0'));
|
|
721
|
+
return `${hex.slice(0, 4).join('')}-${hex.slice(4, 6).join('')}-${hex.slice(6, 8).join('')}-${hex.slice(8, 10).join('')}-${hex.slice(10).join('')}`;
|
|
722
|
+
}
|
|
617
723
|
/**
|
|
618
724
|
* @deprecated Use `getInstanceState` instead. Will be removed in 5.0.0
|
|
619
725
|
* @param obj - The object to get local state from
|
|
@@ -646,6 +752,7 @@ function getInstanceState(obj) {
|
|
|
646
752
|
return undefined;
|
|
647
753
|
return obj.__ngt__ || undefined;
|
|
648
754
|
}
|
|
755
|
+
const pointerEventRegistrations = new WeakMap();
|
|
649
756
|
/**
|
|
650
757
|
* Invalidates an instance, triggering a re-render of the scene.
|
|
651
758
|
*
|
|
@@ -710,7 +817,7 @@ function prepare(object, type, instanceState) {
|
|
|
710
817
|
const [_nonObjects] = [nonObjects(), geometryStamp()];
|
|
711
818
|
return _nonObjects;
|
|
712
819
|
}, ...(ngDevMode ? [{ debugName: "nonObjectsChanged" }] : /* istanbul ignore next */ []));
|
|
713
|
-
instance.__ngt_id__ = crypto.randomUUID();
|
|
820
|
+
instance.__ngt_id__ = typeof crypto.randomUUID === 'function' ? crypto.randomUUID() : uuidv4Fallback();
|
|
714
821
|
instance.__ngt__ = {
|
|
715
822
|
previousAttach: null,
|
|
716
823
|
type,
|
|
@@ -721,16 +828,12 @@ function prepare(object, type, instanceState) {
|
|
|
721
828
|
parent: hierarchyStore.parent,
|
|
722
829
|
objects: hierarchyStore.objects,
|
|
723
830
|
nonObjects: nonObjectsChanged,
|
|
724
|
-
add(object, type) {
|
|
831
|
+
add(object, type, before) {
|
|
725
832
|
const current = instance.__ngt__.hierarchyStore.snapshot[type];
|
|
726
|
-
const
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
}
|
|
731
|
-
else {
|
|
732
|
-
instance.__ngt__.hierarchyStore.update((prev) => ({ [type]: [...prev[type], object] }));
|
|
733
|
-
}
|
|
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 });
|
|
734
837
|
notifyAncestors(instance.__ngt__.hierarchyStore.snapshot.parent, type);
|
|
735
838
|
},
|
|
736
839
|
remove(object, type) {
|
|
@@ -753,26 +856,42 @@ function prepare(object, type, instanceState) {
|
|
|
753
856
|
setPointerEvent: {
|
|
754
857
|
value: (eventName, callback) => {
|
|
755
858
|
const iS = getInstanceState(instance);
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
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;
|
|
768
880
|
iS.eventCount += 1;
|
|
769
|
-
|
|
881
|
+
let active = true;
|
|
770
882
|
return () => {
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
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];
|
|
775
892
|
}
|
|
893
|
+
if (iS.eventCount === 0)
|
|
894
|
+
iS.removeInteraction?.(iS.store);
|
|
776
895
|
};
|
|
777
896
|
},
|
|
778
897
|
configurable: true,
|
|
@@ -808,10 +927,27 @@ function prepare(object, type, instanceState) {
|
|
|
808
927
|
root = root.snapshot.previousRoot;
|
|
809
928
|
}
|
|
810
929
|
if (root.snapshot.internal) {
|
|
811
|
-
const
|
|
812
|
-
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);
|
|
813
934
|
if (index >= 0)
|
|
814
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
|
+
}
|
|
815
951
|
}
|
|
816
952
|
},
|
|
817
953
|
configurable: true,
|
|
@@ -819,7 +955,8 @@ function prepare(object, type, instanceState) {
|
|
|
819
955
|
});
|
|
820
956
|
return instance;
|
|
821
957
|
}
|
|
822
|
-
const
|
|
958
|
+
const pendingAncestorNotifications = new Map();
|
|
959
|
+
let ancestorNotificationScheduled = false;
|
|
823
960
|
/**
|
|
824
961
|
* Notify ancestors about changes to a THREE.js objects' children
|
|
825
962
|
*
|
|
@@ -827,37 +964,41 @@ const notificationCache = new Map();
|
|
|
827
964
|
* in which case the model matrices will be settled later. `NgtsCenter` needs to know about this
|
|
828
965
|
* matrices change to re-center everything inside of it.
|
|
829
966
|
*
|
|
830
|
-
*
|
|
831
|
-
*
|
|
832
|
-
*
|
|
833
|
-
* 1. If there's no cache or
|
|
834
|
-
* 2. If the type is different for the same instance or
|
|
835
|
-
* 3. We've skipped the notifications for this instance more than a certain amount
|
|
836
|
-
*
|
|
837
|
-
* 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.
|
|
838
969
|
*/
|
|
839
970
|
function notifyAncestors(instance, type) {
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
if (notificationCache.size === 1) {
|
|
853
|
-
queueMicrotask(() => notificationCache.clear());
|
|
854
|
-
}
|
|
855
|
-
const { parent } = localState.hierarchyStore.snapshot;
|
|
856
|
-
localState.hierarchyStore.update({ [type]: (localState.hierarchyStore.snapshot[type] || []).slice() });
|
|
857
|
-
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)
|
|
858
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);
|
|
859
1001
|
}
|
|
860
|
-
notificationCache.set(id, { ...cached, skipCount: cached.skipCount + 1 });
|
|
861
1002
|
}
|
|
862
1003
|
|
|
863
1004
|
/**
|
|
@@ -880,9 +1021,9 @@ function diffProps(instance, props) {
|
|
|
880
1021
|
key = 'outputColorSpace';
|
|
881
1022
|
}
|
|
882
1023
|
}
|
|
883
|
-
if (is.equ(propValue, instance
|
|
1024
|
+
if (is.equ(propValue, resolveInstanceKey(instance, key).targetProp))
|
|
884
1025
|
continue;
|
|
885
|
-
changes.push([
|
|
1026
|
+
changes.push([key, propValue]);
|
|
886
1027
|
}
|
|
887
1028
|
return changes;
|
|
888
1029
|
}
|
|
@@ -896,6 +1037,41 @@ const NGT_APPLY_PROPS = '__ngt_apply_props__';
|
|
|
896
1037
|
// https://github.com/mrdoob/three.js/pull/27042
|
|
897
1038
|
// https://github.com/mrdoob/three.js/pull/22748
|
|
898
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
|
+
}
|
|
899
1075
|
/**
|
|
900
1076
|
* Resolves a property key that may contain dot notation (pierced props).
|
|
901
1077
|
*
|
|
@@ -971,11 +1147,7 @@ function applyProps(instance, props) {
|
|
|
971
1147
|
const rootState = localState?.store?.snapshot ?? instance[NGT_APPLY_PROPS]?.snapshot ?? {};
|
|
972
1148
|
const changes = diffProps(instance, props);
|
|
973
1149
|
for (let i = 0; i < changes.length; i++) {
|
|
974
|
-
|
|
975
|
-
// Ignore setting undefined props
|
|
976
|
-
// https://github.com/pmndrs/react-three-fiber/issues/274
|
|
977
|
-
if (value === undefined)
|
|
978
|
-
continue;
|
|
1150
|
+
const [key, nextValue] = changes[i];
|
|
979
1151
|
// Alias (output)encoding => (output)colorSpace (since r152)
|
|
980
1152
|
// https://github.com/pmndrs/react-three-fiber/pull/2829
|
|
981
1153
|
// if (is.colorSpaceExist(instance)) {
|
|
@@ -992,10 +1164,7 @@ function applyProps(instance, props) {
|
|
|
992
1164
|
// }
|
|
993
1165
|
// }
|
|
994
1166
|
const { root, targetKey, targetProp } = resolveInstanceKey(instance, key);
|
|
995
|
-
|
|
996
|
-
if (root !== instance) {
|
|
997
|
-
return applyProps(root, { [targetKey]: value });
|
|
998
|
-
}
|
|
1167
|
+
const value = resolvePropertyValue(root, targetKey, targetProp, nextValue);
|
|
999
1168
|
// Layers have no copy function, we must therefore copy the mask property
|
|
1000
1169
|
if (targetProp instanceof THREE.Layers && value instanceof THREE.Layers) {
|
|
1001
1170
|
targetProp.mask = value.mask;
|
|
@@ -1054,22 +1223,13 @@ function applyProps(instance, props) {
|
|
|
1054
1223
|
}
|
|
1055
1224
|
checkUpdate(root[targetKey]);
|
|
1056
1225
|
checkUpdate(targetProp);
|
|
1057
|
-
invalidateInstance(instance);
|
|
1058
1226
|
}
|
|
1059
|
-
const instanceHandlersCount = localState?.eventCount;
|
|
1060
1227
|
const parent = localState?.hierarchyStore?.snapshot.parent;
|
|
1061
|
-
if (parent && rootState.internal && instance['raycast'] && instanceHandlersCount !== localState?.eventCount) {
|
|
1062
|
-
// Pre-emptively remove the instance from the interaction manager
|
|
1063
|
-
const index = rootState.internal.interaction.indexOf(instance);
|
|
1064
|
-
if (index > -1)
|
|
1065
|
-
rootState.internal.interaction.splice(index, 1);
|
|
1066
|
-
// Add the instance to the interaction manager only when it has handlers
|
|
1067
|
-
if (localState?.eventCount)
|
|
1068
|
-
rootState.internal.interaction.push(instance);
|
|
1069
|
-
}
|
|
1070
1228
|
if (parent && localState?.onUpdate && changes.length) {
|
|
1071
1229
|
localState.onUpdate(instance);
|
|
1072
1230
|
}
|
|
1231
|
+
if (changes.length)
|
|
1232
|
+
invalidateInstance(instance);
|
|
1073
1233
|
// clearing the intermediate store from the instance
|
|
1074
1234
|
if (instance[NGT_APPLY_PROPS])
|
|
1075
1235
|
delete instance[NGT_APPLY_PROPS];
|
|
@@ -1083,6 +1243,7 @@ function applyProps(instance, props) {
|
|
|
1083
1243
|
* allowing the custom renderer to instantiate objects when elements are created.
|
|
1084
1244
|
*/
|
|
1085
1245
|
const catalogue = {};
|
|
1246
|
+
const catalogueOwnership = new Map();
|
|
1086
1247
|
/**
|
|
1087
1248
|
* Registers Three.js constructors for use in templates.
|
|
1088
1249
|
*
|
|
@@ -1108,10 +1269,59 @@ const catalogue = {};
|
|
|
1108
1269
|
* ```
|
|
1109
1270
|
*/
|
|
1110
1271
|
function extend(objects) {
|
|
1111
|
-
const
|
|
1112
|
-
|
|
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;
|
|
1113
1296
|
return () => {
|
|
1114
|
-
|
|
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
|
+
}
|
|
1115
1325
|
};
|
|
1116
1326
|
}
|
|
1117
1327
|
/**
|
|
@@ -1121,6 +1331,9 @@ function extend(objects) {
|
|
|
1121
1331
|
*/
|
|
1122
1332
|
function remove(...keys) {
|
|
1123
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);
|
|
1124
1337
|
delete catalogue[key];
|
|
1125
1338
|
}
|
|
1126
1339
|
}
|
|
@@ -1137,34 +1350,6 @@ function injectCatalogue() {
|
|
|
1137
1350
|
return inject(NGT_CATALOGUE);
|
|
1138
1351
|
}
|
|
1139
1352
|
|
|
1140
|
-
function isRendererNode(node) {
|
|
1141
|
-
return !!node && typeof node === 'object' && NGT_RENDERER_NODE_FLAG in node;
|
|
1142
|
-
}
|
|
1143
|
-
function createRendererNode(type, node, document) {
|
|
1144
|
-
const state = [type, false, undefined, undefined, undefined, undefined, []];
|
|
1145
|
-
const rendererNode = Object.assign(node, { [NGT_RENDERER_NODE_FLAG]: state });
|
|
1146
|
-
// NOTE: assign ownerDocument to node so we can use HostListener in Component
|
|
1147
|
-
if (!rendererNode['ownerDocument'])
|
|
1148
|
-
rendererNode['ownerDocument'] = document;
|
|
1149
|
-
// NOTE: Angular SSR calls `node.getAttribute()` to retrieve hydration info on a node
|
|
1150
|
-
if (!('getAttribute' in rendererNode) || typeof rendererNode['getAttribute'] !== 'function') {
|
|
1151
|
-
const getNodeAttribute = (name) => rendererNode[name];
|
|
1152
|
-
getNodeAttribute[NGT_GET_NODE_ATTRIBUTE_FLAG] = true;
|
|
1153
|
-
Object.defineProperty(rendererNode, 'getAttribute', { value: getNodeAttribute, configurable: true });
|
|
1154
|
-
}
|
|
1155
|
-
return rendererNode;
|
|
1156
|
-
}
|
|
1157
|
-
function setRendererParentNode(node, parent) {
|
|
1158
|
-
if (!node.__ngt_renderer__[5 /* NgtRendererClassId.parent */]) {
|
|
1159
|
-
node.__ngt_renderer__[5 /* NgtRendererClassId.parent */] = parent;
|
|
1160
|
-
}
|
|
1161
|
-
}
|
|
1162
|
-
function addRendererChildNode(node, child) {
|
|
1163
|
-
if (!node.__ngt_renderer__[6 /* NgtRendererClassId.children */].includes(child)) {
|
|
1164
|
-
node.__ngt_renderer__[6 /* NgtRendererClassId.children */].push(child);
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
1353
|
const idCache = {};
|
|
1169
1354
|
/**
|
|
1170
1355
|
* Generates a unique identifier.
|
|
@@ -1271,6 +1456,7 @@ function makeObjectGraph(object) {
|
|
|
1271
1456
|
return data;
|
|
1272
1457
|
}
|
|
1273
1458
|
|
|
1459
|
+
const NGT_EVENT_LAYER = Symbol('NGT_EVENT_LAYER');
|
|
1274
1460
|
/**
|
|
1275
1461
|
* @fileoverview Event handling system for Angular Three.
|
|
1276
1462
|
*
|
|
@@ -1364,52 +1550,57 @@ function createEvents(store) {
|
|
|
1364
1550
|
if (!current)
|
|
1365
1551
|
eventsObjects.push(eventsObject);
|
|
1366
1552
|
}
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
const objectRootState = getInstanceState(eventsObjects[i])?.store?.snapshot;
|
|
1378
|
-
if (objectRootState) {
|
|
1379
|
-
objectRootState.raycaster.camera = undefined;
|
|
1380
|
-
}
|
|
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]);
|
|
1381
1563
|
}
|
|
1382
|
-
//
|
|
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;
|
|
1383
1582
|
const raycastResults = [];
|
|
1384
|
-
|
|
1385
|
-
const
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
// If the camera is still undefined we have to skip this layer entirely
|
|
1394
|
-
if (objState.raycaster.camera === undefined)
|
|
1395
|
-
objState.raycaster.camera = null;
|
|
1396
|
-
}
|
|
1397
|
-
// Intersect object by object
|
|
1398
|
-
return objState.raycaster.camera ? objState.raycaster.intersectObject(obj, true) : [];
|
|
1399
|
-
}
|
|
1400
|
-
// Collect events
|
|
1401
|
-
for (let i = 0; i < eventsObjectsLen; i++) {
|
|
1402
|
-
const objResults = handleRaycast(eventsObjects[i]);
|
|
1403
|
-
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)
|
|
1404
1592
|
continue;
|
|
1405
|
-
|
|
1406
|
-
|
|
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);
|
|
1407
1598
|
}
|
|
1408
1599
|
}
|
|
1409
1600
|
// Sort by event priority and distance
|
|
1410
1601
|
raycastResults.sort((a, b) => {
|
|
1411
|
-
const aState = getInstanceState(a.object)?.store?.snapshot;
|
|
1412
|
-
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;
|
|
1413
1604
|
if (!aState || !bState)
|
|
1414
1605
|
return a.distance - b.distance;
|
|
1415
1606
|
return bState.events.priority - aState.events.priority || a.distance - b.distance;
|
|
@@ -1471,7 +1662,8 @@ function createEvents(store) {
|
|
|
1471
1662
|
return;
|
|
1472
1663
|
});
|
|
1473
1664
|
}
|
|
1474
|
-
const
|
|
1665
|
+
const eventLayer = hit[NGT_EVENT_LAYER];
|
|
1666
|
+
const { raycaster, pointer, camera, internal } = eventLayer?.snapshot || instanceState?.store?.snapshot || rootState;
|
|
1475
1667
|
const unprojectedPoint = new THREE.Vector3(pointer.x, pointer.y, 0).unproject(camera);
|
|
1476
1668
|
const hasPointerCapture = (id) => internal.capturedMap.get(id)?.has(hit.eventObject) ?? false;
|
|
1477
1669
|
const setPointerCapture = (id) => {
|
|
@@ -1618,6 +1810,15 @@ function createEvents(store) {
|
|
|
1618
1810
|
const hits = intersect(event, filter);
|
|
1619
1811
|
// Only calculate distance for click events to avoid unnecessary math
|
|
1620
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
|
+
};
|
|
1621
1822
|
// Save initial coordinates on pointer-down
|
|
1622
1823
|
if (name === 'pointerdown') {
|
|
1623
1824
|
internal.initialClick = [event.offsetX, event.offsetY];
|
|
@@ -1675,23 +1876,13 @@ function createEvents(store) {
|
|
|
1675
1876
|
// Forward all events back to their respective handlers with the exception of click events,
|
|
1676
1877
|
// which must use the initial target
|
|
1677
1878
|
if (!isClickEvent || internal.initialHits.includes(eventObject)) {
|
|
1678
|
-
|
|
1679
|
-
const missedObjects = internal.interaction.filter((object) => !internal.initialHits.includes(object));
|
|
1680
|
-
// Call pointerMissed only if we have objects to notify
|
|
1681
|
-
if (missedObjects.length > 0) {
|
|
1682
|
-
pointerMissed(event, missedObjects);
|
|
1683
|
-
}
|
|
1879
|
+
notifyMissedOnce();
|
|
1684
1880
|
// Now call the handler
|
|
1685
1881
|
handler(data);
|
|
1686
1882
|
}
|
|
1687
1883
|
}
|
|
1688
1884
|
else if (isClickEvent && internal.initialHits.includes(eventObject)) {
|
|
1689
|
-
|
|
1690
|
-
const missedObjects = internal.interaction.filter((object) => !internal.initialHits.includes(object));
|
|
1691
|
-
// Call pointerMissed only if we have objects to notify
|
|
1692
|
-
if (missedObjects.length > 0) {
|
|
1693
|
-
pointerMissed(event, missedObjects);
|
|
1694
|
-
}
|
|
1885
|
+
notifyMissedOnce();
|
|
1695
1886
|
}
|
|
1696
1887
|
}
|
|
1697
1888
|
}
|
|
@@ -1750,10 +1941,22 @@ function attach(object, value, paths = [], useApplyProps = false) {
|
|
|
1750
1941
|
function detach(parent, child, attachProp) {
|
|
1751
1942
|
const childInstanceState = getInstanceState(child);
|
|
1752
1943
|
if (childInstanceState) {
|
|
1753
|
-
|
|
1754
|
-
|
|
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
|
+
}
|
|
1755
1958
|
else
|
|
1756
|
-
|
|
1959
|
+
previousAttach?.();
|
|
1757
1960
|
}
|
|
1758
1961
|
}
|
|
1759
1962
|
function assignEmpty(obj, base, shouldAssignStoreForApplyProps = false) {
|
|
@@ -1813,18 +2016,21 @@ function kebabToPascal(str) {
|
|
|
1813
2016
|
}
|
|
1814
2017
|
return pascalStr;
|
|
1815
2018
|
}
|
|
1816
|
-
function propagateStoreRecursively(node, parentNode) {
|
|
2019
|
+
function propagateStoreRecursively(node, parentNode, storeOverride) {
|
|
1817
2020
|
const iS = getInstanceState(node);
|
|
1818
2021
|
const pIS = getInstanceState(parentNode);
|
|
1819
2022
|
if (!iS || !pIS)
|
|
1820
2023
|
return;
|
|
2024
|
+
const store = storeOverride ?? pIS.store;
|
|
2025
|
+
if (!store)
|
|
2026
|
+
return;
|
|
1821
2027
|
// assign store on child if not already exist
|
|
1822
2028
|
// or child store is not the same as parent store
|
|
1823
2029
|
// or child store is the parent of parent store
|
|
1824
|
-
if (!iS.store || iS.store !==
|
|
1825
|
-
iS.store =
|
|
2030
|
+
if (!iS.store || iS.store !== store || iS.store === store.snapshot.previousRoot) {
|
|
2031
|
+
iS.store = store;
|
|
1826
2032
|
// Call addInteraction if it exists
|
|
1827
|
-
iS.addInteraction?.(
|
|
2033
|
+
iS.addInteraction?.(store);
|
|
1828
2034
|
// Collect all children (objects and nonObjects)
|
|
1829
2035
|
const children = [
|
|
1830
2036
|
...(iS.objects ? untracked(iS.objects) : []),
|
|
@@ -1832,60 +2038,88 @@ function propagateStoreRecursively(node, parentNode) {
|
|
|
1832
2038
|
];
|
|
1833
2039
|
// Recursively reassign the store for each child
|
|
1834
2040
|
for (const child of children) {
|
|
1835
|
-
propagateStoreRecursively(child, node);
|
|
2041
|
+
propagateStoreRecursively(child, node, store);
|
|
1836
2042
|
}
|
|
1837
2043
|
}
|
|
1838
2044
|
}
|
|
1839
|
-
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) {
|
|
1840
2057
|
const pIS = getInstanceState(parent);
|
|
1841
2058
|
const cIS = getInstanceState(child);
|
|
1842
2059
|
if (!pIS || !cIS) {
|
|
1843
2060
|
throw new Error(`[NGT] THREE instances need to be prepared with local state.`);
|
|
1844
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
|
+
}
|
|
1845
2078
|
// whether the child is added to the parent with parent.add()
|
|
1846
2079
|
let added = false;
|
|
1847
|
-
// propagate store recursively
|
|
1848
|
-
propagateStoreRecursively(child, parent);
|
|
1849
2080
|
if (cIS.attach) {
|
|
1850
2081
|
const attachProp = cIS.attach;
|
|
1851
2082
|
if (typeof attachProp === 'function') {
|
|
1852
2083
|
let attachCleanUp = undefined;
|
|
1853
2084
|
if (cIS.type === 'ngt-value') {
|
|
1854
|
-
if (cIS.hierarchyStore.snapshot.parent !== parent) {
|
|
1855
|
-
cIS.setParent(parent);
|
|
1856
|
-
}
|
|
1857
2085
|
// at this point we don't have rawValue yet, so we bail and wait until the Renderer recalls attach
|
|
1858
2086
|
if (child.__ngt_renderer__[2 /* NgtRendererClassId.rawValue */] === undefined)
|
|
1859
2087
|
return;
|
|
2088
|
+
if (cIS.hierarchyStore.snapshot.parent !== parent) {
|
|
2089
|
+
cIS.setParent(parent);
|
|
2090
|
+
}
|
|
1860
2091
|
attachCleanUp = attachProp(parent, child.__ngt_renderer__[2 /* NgtRendererClassId.rawValue */], cIS.store);
|
|
1861
2092
|
}
|
|
1862
2093
|
else {
|
|
1863
2094
|
attachCleanUp = attachProp(parent, child, cIS.store);
|
|
1864
2095
|
}
|
|
1865
|
-
|
|
1866
|
-
cIS.previousAttach = attachCleanUp;
|
|
2096
|
+
cIS.previousAttach = attachCleanUp;
|
|
1867
2097
|
}
|
|
1868
2098
|
else {
|
|
1869
2099
|
// we skip attach none if set explicitly
|
|
1870
2100
|
if (attachProp[0] === 'none') {
|
|
2101
|
+
cIS.previousAttach = undefined;
|
|
2102
|
+
pIS.add?.(child, 'nonObjects', before);
|
|
2103
|
+
cIS.setParent(parent);
|
|
1871
2104
|
invalidateInstance(child);
|
|
2105
|
+
invalidateInstance(parent);
|
|
1872
2106
|
return;
|
|
1873
2107
|
}
|
|
1874
2108
|
// handle material array
|
|
1875
2109
|
if (attachProp[0] === 'material' &&
|
|
1876
|
-
attachProp[1] &&
|
|
1877
|
-
|
|
2110
|
+
attachProp[1] !== undefined &&
|
|
2111
|
+
!Number.isNaN(Number(attachProp[1])) &&
|
|
1878
2112
|
is.three(child, 'isMaterial') &&
|
|
1879
2113
|
!Array.isArray(parent['material'])) {
|
|
1880
2114
|
parent['material'] = [];
|
|
1881
2115
|
}
|
|
1882
2116
|
if (cIS.type === 'ngt-value') {
|
|
1883
|
-
if (cIS.hierarchyStore.snapshot.parent !== parent) {
|
|
1884
|
-
cIS.setParent(parent);
|
|
1885
|
-
}
|
|
1886
2117
|
// at this point we don't have rawValue yet, so we bail and wait until the Renderer recalls attach
|
|
1887
2118
|
if (child.__ngt_renderer__[2 /* NgtRendererClassId.rawValue */] === undefined)
|
|
1888
2119
|
return;
|
|
2120
|
+
if (cIS.hierarchyStore.snapshot.parent !== parent) {
|
|
2121
|
+
cIS.setParent(parent);
|
|
2122
|
+
}
|
|
1889
2123
|
// save prev value
|
|
1890
2124
|
cIS.previousAttach = attachProp.reduce((value, key) => value[key], parent);
|
|
1891
2125
|
attach(parent, child.__ngt_renderer__[2 /* NgtRendererClassId.rawValue */], attachProp, true);
|
|
@@ -1898,12 +2132,12 @@ function attachThreeNodes(parent, child) {
|
|
|
1898
2132
|
}
|
|
1899
2133
|
}
|
|
1900
2134
|
else if (is.three(parent, 'isObject3D') && is.three(child, 'isObject3D')) {
|
|
1901
|
-
parent
|
|
2135
|
+
placeObject3DChild(parent, child, before);
|
|
1902
2136
|
added = true;
|
|
1903
2137
|
cIS.addInteraction?.(cIS.store || pIS.store);
|
|
1904
2138
|
}
|
|
1905
2139
|
if (pIS.add) {
|
|
1906
|
-
pIS.add(child, added ? 'objects' : 'nonObjects');
|
|
2140
|
+
pIS.add(child, added ? 'objects' : 'nonObjects', before);
|
|
1907
2141
|
}
|
|
1908
2142
|
if (cIS.parent && untracked(cIS.parent) !== parent) {
|
|
1909
2143
|
cIS.setParent(parent);
|
|
@@ -1915,7 +2149,7 @@ function attachThreeNodes(parent, child) {
|
|
|
1915
2149
|
invalidateInstance(child);
|
|
1916
2150
|
invalidateInstance(parent);
|
|
1917
2151
|
}
|
|
1918
|
-
function removeThreeChild(child, parent, dispose) {
|
|
2152
|
+
function removeThreeChild(child, parent, dispose = false) {
|
|
1919
2153
|
const pIS = getInstanceState(parent);
|
|
1920
2154
|
const cIS = getInstanceState(child);
|
|
1921
2155
|
// clear parent ref
|
|
@@ -1935,8 +2169,9 @@ function removeThreeChild(child, parent, dispose) {
|
|
|
1935
2169
|
}
|
|
1936
2170
|
// dispose
|
|
1937
2171
|
const isPrimitive = cIS?.type && cIS.type === 'ngt-primitive';
|
|
1938
|
-
if (!isPrimitive && child['dispose'] && !is.three(child, 'isScene')) {
|
|
1939
|
-
|
|
2172
|
+
if (dispose && !isPrimitive && child['dispose'] && !is.three(child, 'isScene')) {
|
|
2173
|
+
const disposeInstance = child['dispose'].bind(child);
|
|
2174
|
+
queueMicrotask(disposeInstance);
|
|
1940
2175
|
}
|
|
1941
2176
|
invalidateInstance(parent);
|
|
1942
2177
|
}
|
|
@@ -1944,18 +2179,31 @@ function internalDestroyNode(node, removeChild) {
|
|
|
1944
2179
|
const rS = node.__ngt_renderer__;
|
|
1945
2180
|
if (!rS || rS[1 /* NgtRendererClassId.destroyed */])
|
|
1946
2181
|
return;
|
|
1947
|
-
|
|
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 */];
|
|
1948
2190
|
removeChild?.(node, child);
|
|
2191
|
+
if (destructionOwner && childOwner && childOwner !== destructionOwner)
|
|
2192
|
+
continue;
|
|
1949
2193
|
internalDestroyNode(child, removeChild);
|
|
1950
2194
|
}
|
|
1951
2195
|
// clear out parent if haven't
|
|
1952
|
-
rS[
|
|
2196
|
+
rS[4 /* NgtRendererClassId.parent */] = undefined;
|
|
1953
2197
|
// clear out children
|
|
1954
|
-
rS[
|
|
2198
|
+
rS[5 /* NgtRendererClassId.children */].length = 0;
|
|
1955
2199
|
// clear out NgtInstanceState
|
|
1956
|
-
const iS = getInstanceState(node);
|
|
1957
2200
|
if (iS) {
|
|
1958
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
|
+
}
|
|
1959
2207
|
iS.removeInteraction?.(iS.store);
|
|
1960
2208
|
delete temp['onAttach'];
|
|
1961
2209
|
delete temp['onUpdate'];
|
|
@@ -1978,13 +2226,12 @@ function internalDestroyNode(node, removeChild) {
|
|
|
1978
2226
|
delete node['__ngt__'];
|
|
1979
2227
|
}
|
|
1980
2228
|
}
|
|
1981
|
-
// clear
|
|
1982
|
-
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();
|
|
1983
2234
|
if (rS[0 /* NgtRendererClassId.type */] === 'comment') {
|
|
1984
|
-
delete node[NGT_INTERNAL_ADD_COMMENT_FLAG];
|
|
1985
|
-
delete node[NGT_INTERNAL_SET_PARENT_COMMENT_FLAG];
|
|
1986
|
-
delete node[NGT_CANVAS_CONTENT_FLAG];
|
|
1987
|
-
delete node[NGT_PORTAL_CONTENT_FLAG];
|
|
1988
2235
|
delete node[NGT_DOM_PARENT_FLAG];
|
|
1989
2236
|
}
|
|
1990
2237
|
// clear getAttribute if exist
|
|
@@ -1997,10 +2244,19 @@ function internalDestroyNode(node, removeChild) {
|
|
|
1997
2244
|
rS[1 /* NgtRendererClassId.destroyed */] = true;
|
|
1998
2245
|
}
|
|
1999
2246
|
|
|
2247
|
+
var _a$1;
|
|
2000
2248
|
/**
|
|
2001
2249
|
* Injection token for renderer factory options.
|
|
2002
2250
|
*/
|
|
2003
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
|
+
}
|
|
2004
2260
|
/**
|
|
2005
2261
|
* Angular renderer factory for Three.js elements.
|
|
2006
2262
|
*
|
|
@@ -2029,46 +2285,45 @@ class NgtRendererFactory2 {
|
|
|
2029
2285
|
this.catalogue = injectCatalogue();
|
|
2030
2286
|
this.document = inject(DOCUMENT);
|
|
2031
2287
|
this.options = inject(NGT_RENDERER_OPTIONS, { optional: true }) || {};
|
|
2032
|
-
this.
|
|
2288
|
+
this.rendererByDelegate = new WeakMap();
|
|
2033
2289
|
}
|
|
2034
2290
|
createRenderer(hostElement, type) {
|
|
2035
2291
|
const delegateRenderer = this.delegateRendererFactory.createRenderer(hostElement, type);
|
|
2036
2292
|
if (!type)
|
|
2037
2293
|
return delegateRenderer;
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
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);
|
|
2044
2306
|
}
|
|
2045
2307
|
}
|
|
2046
|
-
|
|
2308
|
+
markRendererViewHost(hostRendererNode);
|
|
2047
2309
|
}
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
this.rendererMap.set(type.id, delegateRenderer);
|
|
2053
|
-
// patch delegate destroyNode so we can destroy this HTML node
|
|
2054
|
-
// TODO: make sure we really need to do this
|
|
2055
|
-
const originalDestroyNode = delegateRenderer.destroyNode?.bind(delegateRenderer);
|
|
2056
|
-
if (!originalDestroyNode || !(NGT_DELEGATE_RENDERER_DESTROY_NODE_PATCHED_FLAG in originalDestroyNode)) {
|
|
2057
|
-
delegateRenderer.destroyNode = (node) => {
|
|
2058
|
-
originalDestroyNode?.(node);
|
|
2059
|
-
if (node !== hostElement)
|
|
2060
|
-
return;
|
|
2061
|
-
internalDestroyNode(node, null);
|
|
2062
|
-
};
|
|
2063
|
-
Object.assign(delegateRenderer.destroyNode, {
|
|
2064
|
-
[NGT_DELEGATE_RENDERER_DESTROY_NODE_PATCHED_FLAG]: true,
|
|
2065
|
-
});
|
|
2066
|
-
}
|
|
2067
|
-
return delegateRenderer;
|
|
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);
|
|
2068
2314
|
}
|
|
2069
|
-
this.rendererMap.set(type.id, (renderer = new NgtRenderer2(delegateRenderer, this.catalogue, this.document, this.options)));
|
|
2070
2315
|
return renderer;
|
|
2071
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
|
+
}
|
|
2072
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 }); }
|
|
2073
2328
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: NgtRendererFactory2 }); }
|
|
2074
2329
|
}
|
|
@@ -2088,40 +2343,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
2088
2343
|
* @internal
|
|
2089
2344
|
*/
|
|
2090
2345
|
class NgtRenderer2 {
|
|
2091
|
-
|
|
2346
|
+
static { _a$1 = NGT_RENDERER_CONTEXT_FLAG; }
|
|
2347
|
+
constructor(delegateRenderer, catalogue, document, options) {
|
|
2092
2348
|
this.delegateRenderer = delegateRenderer;
|
|
2093
2349
|
this.catalogue = catalogue;
|
|
2094
2350
|
this.document = document;
|
|
2095
2351
|
this.options = options;
|
|
2096
|
-
this.
|
|
2097
|
-
this.
|
|
2098
|
-
this.
|
|
2099
|
-
this.
|
|
2100
|
-
|
|
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
|
+
}
|
|
2101
2364
|
};
|
|
2102
|
-
this.
|
|
2103
|
-
this.removeClass = this.delegateRenderer.removeClass.bind(this.delegateRenderer);
|
|
2104
|
-
this.setStyle = this.delegateRenderer.setStyle.bind(this.delegateRenderer);
|
|
2105
|
-
this.removeStyle = this.delegateRenderer.removeStyle.bind(this.delegateRenderer);
|
|
2106
|
-
this.selectRootElement = this.delegateRenderer.selectRootElement.bind(this.delegateRenderer);
|
|
2107
|
-
this.nextSibling = this.delegateRenderer.nextSibling.bind(this.delegateRenderer);
|
|
2108
|
-
this.setValue = this.delegateRenderer.setValue.bind(this.delegateRenderer);
|
|
2365
|
+
this.data = { ...this.delegateRenderer.data, __ngt_renderer__: true };
|
|
2109
2366
|
if (!this.options.verbose) {
|
|
2110
2367
|
this.options.verbose = false;
|
|
2111
2368
|
}
|
|
2112
2369
|
}
|
|
2113
|
-
get data() {
|
|
2114
|
-
return { ...this.delegateRenderer.data, __ngt_renderer__: true };
|
|
2115
|
-
}
|
|
2116
2370
|
destroy() {
|
|
2117
|
-
|
|
2118
|
-
this.count -= 1;
|
|
2119
|
-
return;
|
|
2120
|
-
}
|
|
2121
|
-
// this is the last instance of the same NgtRenderer2
|
|
2122
|
-
this.count = 0;
|
|
2123
|
-
this.argsInjectors = [];
|
|
2124
|
-
this.parentInjectors = [];
|
|
2371
|
+
this.delegateRenderer.destroy();
|
|
2125
2372
|
}
|
|
2126
2373
|
createElement(name, namespace) {
|
|
2127
2374
|
const platformElement = this.delegateRenderer.createElement(name, namespace);
|
|
@@ -2132,8 +2379,8 @@ class NgtRenderer2 {
|
|
|
2132
2379
|
return createRendererNode('three', prepare(platformElement, 'ngt-value'), this.document);
|
|
2133
2380
|
}
|
|
2134
2381
|
const [injectedArgs, injectedParent] = [
|
|
2135
|
-
this.getNgtDirective(NgtArgs
|
|
2136
|
-
this.getNgtDirective(NgtParent
|
|
2382
|
+
this.getNgtDirective(NgtArgs)?.value || [],
|
|
2383
|
+
this.getNgtDirective(NgtParent)?.value,
|
|
2137
2384
|
];
|
|
2138
2385
|
if (name === 'ngt-primitive') {
|
|
2139
2386
|
if (!injectedArgs[0])
|
|
@@ -2145,7 +2392,7 @@ class NgtRenderer2 {
|
|
|
2145
2392
|
prepare(object, 'ngt-primitive');
|
|
2146
2393
|
const primitiveRendererNode = createRendererNode('three', object, this.document);
|
|
2147
2394
|
if (injectedParent) {
|
|
2148
|
-
primitiveRendererNode.__ngt_renderer__[
|
|
2395
|
+
primitiveRendererNode.__ngt_renderer__[6 /* NgtRendererClassId.parentOverride */] =
|
|
2149
2396
|
injectedParent;
|
|
2150
2397
|
}
|
|
2151
2398
|
return primitiveRendererNode;
|
|
@@ -2175,7 +2422,7 @@ class NgtRenderer2 {
|
|
|
2175
2422
|
instanceState.attach = ['material'];
|
|
2176
2423
|
}
|
|
2177
2424
|
if (injectedParent) {
|
|
2178
|
-
rendererNode.__ngt_renderer__[
|
|
2425
|
+
rendererNode.__ngt_renderer__[6 /* NgtRendererClassId.parentOverride */] =
|
|
2179
2426
|
injectedParent;
|
|
2180
2427
|
}
|
|
2181
2428
|
return rendererNode;
|
|
@@ -2185,31 +2432,24 @@ class NgtRenderer2 {
|
|
|
2185
2432
|
createComment(value) {
|
|
2186
2433
|
const commentNode = this.delegateRenderer.createComment(value);
|
|
2187
2434
|
const commentRendererNode = createRendererNode('comment', commentNode, this.document);
|
|
2188
|
-
// NOTE: we attach an arrow function to the Comment node
|
|
2189
|
-
// In our directives, we can call this function to then start tracking the RendererNode
|
|
2190
|
-
// this is done to limit the amount of Nodes we need to process for getCreationState
|
|
2191
|
-
Object.assign(commentRendererNode, {
|
|
2192
|
-
[NGT_INTERNAL_ADD_COMMENT_FLAG]: (type, injector) => {
|
|
2193
|
-
if (type === 'args') {
|
|
2194
|
-
this.argsInjectors.push(injector);
|
|
2195
|
-
}
|
|
2196
|
-
else if (type === 'parent') {
|
|
2197
|
-
Object.assign(commentRendererNode, {
|
|
2198
|
-
[NGT_INTERNAL_SET_PARENT_COMMENT_FLAG]: (ngtParent) => {
|
|
2199
|
-
commentRendererNode.__ngt_renderer__[5 /* NgtRendererClassId.parent */] = ngtParent;
|
|
2200
|
-
},
|
|
2201
|
-
});
|
|
2202
|
-
this.parentInjectors.push(injector);
|
|
2203
|
-
}
|
|
2204
|
-
commentRendererNode.__ngt_renderer__[4 /* NgtRendererClassId.injector */] = injector;
|
|
2205
|
-
},
|
|
2206
|
-
});
|
|
2207
2435
|
return commentRendererNode;
|
|
2208
2436
|
}
|
|
2209
2437
|
createText(value) {
|
|
2210
2438
|
const textNode = this.delegateRenderer.createText(value);
|
|
2211
2439
|
return createRendererNode('text', textNode, this.document);
|
|
2212
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
|
+
}
|
|
2213
2453
|
appendChild(parent, newChild, refChild, isMove) {
|
|
2214
2454
|
const delegatedFn = refChild
|
|
2215
2455
|
? this.delegateRenderer.insertBefore.bind(this.delegateRenderer, parent, newChild, refChild, isMove)
|
|
@@ -2221,10 +2461,13 @@ class NgtRenderer2 {
|
|
|
2221
2461
|
console.warn('[NGT dev mode] One of parent or child is not a renderer node.', { parent, newChild });
|
|
2222
2462
|
return delegatedFn();
|
|
2223
2463
|
}
|
|
2464
|
+
if (parent === newChild || refChild === newChild)
|
|
2465
|
+
return;
|
|
2466
|
+
this.setNodeRelationship(parent, newChild, refChild);
|
|
2224
2467
|
if (cRS[0 /* NgtRendererClassId.type */] === 'comment') {
|
|
2225
|
-
//
|
|
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.
|
|
2226
2470
|
// comment usually means it's part of a templateRef ViewContainerRef or structural directive
|
|
2227
|
-
setRendererParentNode(newChild, parent);
|
|
2228
2471
|
// if parent is not three, we'll delegate to the renderer
|
|
2229
2472
|
if (pRS[0 /* NgtRendererClassId.type */] !== 'three') {
|
|
2230
2473
|
delegatedFn();
|
|
@@ -2232,52 +2475,43 @@ class NgtRenderer2 {
|
|
|
2232
2475
|
return;
|
|
2233
2476
|
}
|
|
2234
2477
|
if (pRS[0 /* NgtRendererClassId.type */] === 'platform' && cRS[0 /* NgtRendererClassId.type */] === 'platform') {
|
|
2235
|
-
|
|
2236
|
-
|
|
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);
|
|
2237
2485
|
}
|
|
2238
|
-
|
|
2239
|
-
|
|
2486
|
+
else {
|
|
2487
|
+
delegatedFn();
|
|
2240
2488
|
}
|
|
2241
|
-
|
|
2489
|
+
if (threeParent)
|
|
2490
|
+
this.attachThreeDescendants(threeParent, newChild, insertionReference, portalStore);
|
|
2491
|
+
return;
|
|
2242
2492
|
}
|
|
2243
|
-
if (
|
|
2244
|
-
return this.appendThreeRendererNodes(parent, newChild);
|
|
2493
|
+
if (isRendererNodeType(parent, 'three') && isRendererNodeType(newChild, 'three')) {
|
|
2494
|
+
return this.appendThreeRendererNodes(parent, newChild, refChild);
|
|
2245
2495
|
}
|
|
2246
|
-
if (pRS[0 /* NgtRendererClassId.type */] === 'platform' &&
|
|
2247
|
-
|
|
2248
|
-
if (
|
|
2249
|
-
|
|
2250
|
-
addRendererChildNode(parent, newChild);
|
|
2251
|
-
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));
|
|
2252
2500
|
}
|
|
2253
|
-
// platform can also have normal parentNode
|
|
2254
|
-
const platformParentNode = this.delegateRenderer.parentNode(parent);
|
|
2255
|
-
if (platformParentNode) {
|
|
2256
|
-
return this.appendChild(platformParentNode, newChild);
|
|
2257
|
-
}
|
|
2258
|
-
// if not, set up parent and child relationship for this pair then bail
|
|
2259
|
-
this.setNodeRelationship(parent, newChild);
|
|
2260
2501
|
return;
|
|
2261
2502
|
}
|
|
2262
|
-
if (
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
this.
|
|
2268
|
-
}
|
|
2269
|
-
for (const platformChildNode of newChild['childNodes'] || []) {
|
|
2270
|
-
if (!isRendererNode(platformChildNode) ||
|
|
2271
|
-
platformChildNode.__ngt_renderer__[0 /* NgtRendererClassId.type */] !== 'platform')
|
|
2272
|
-
continue;
|
|
2273
|
-
this.appendChild(parent, platformChildNode);
|
|
2274
|
-
}
|
|
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);
|
|
2275
2509
|
return;
|
|
2276
2510
|
}
|
|
2277
|
-
if (
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
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));
|
|
2281
2515
|
return;
|
|
2282
2516
|
}
|
|
2283
2517
|
if (pRS[0 /* NgtRendererClassId.type */] === 'platform' && cRS[0 /* NgtRendererClassId.type */] === 'portal') {
|
|
@@ -2286,14 +2520,12 @@ class NgtRenderer2 {
|
|
|
2286
2520
|
return delegatedFn();
|
|
2287
2521
|
}
|
|
2288
2522
|
insertBefore(parent, newChild, refChild, isMove) {
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
newChild instanceof Comment) {
|
|
2296
|
-
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);
|
|
2297
2529
|
}
|
|
2298
2530
|
// if there is no parent, we delegate
|
|
2299
2531
|
if (!parent) {
|
|
@@ -2301,21 +2533,19 @@ class NgtRenderer2 {
|
|
|
2301
2533
|
}
|
|
2302
2534
|
return this.appendChild(parent, newChild, refChild, isMove);
|
|
2303
2535
|
}
|
|
2304
|
-
removeChild(parent, oldChild, isHostElement) {
|
|
2536
|
+
removeChild(parent, oldChild, isHostElement, requireSynchronousElementRemoval) {
|
|
2305
2537
|
if (parent === null) {
|
|
2306
2538
|
parent = this.parentNode(oldChild);
|
|
2307
2539
|
}
|
|
2308
2540
|
const cRS = oldChild.__ngt_renderer__;
|
|
2309
2541
|
if (!cRS) {
|
|
2310
2542
|
try {
|
|
2311
|
-
return this.delegateRenderer.removeChild(parent, oldChild, isHostElement);
|
|
2543
|
+
return this.delegateRenderer.removeChild(parent, oldChild, isHostElement, requireSynchronousElementRemoval);
|
|
2312
2544
|
}
|
|
2313
2545
|
catch {
|
|
2314
2546
|
return;
|
|
2315
2547
|
}
|
|
2316
2548
|
}
|
|
2317
|
-
// disassociate things from oldChild
|
|
2318
|
-
cRS[5 /* NgtRendererClassId.parent */] = null;
|
|
2319
2549
|
// if parent is still undefined
|
|
2320
2550
|
if (parent == null) {
|
|
2321
2551
|
if (cRS[1 /* NgtRendererClassId.destroyed */]) {
|
|
@@ -2328,39 +2558,38 @@ class NgtRenderer2 {
|
|
|
2328
2558
|
}
|
|
2329
2559
|
const pRS = parent.__ngt_renderer__;
|
|
2330
2560
|
if (!pRS) {
|
|
2331
|
-
return this.delegateRenderer.removeChild(parent, oldChild, isHostElement);
|
|
2561
|
+
return this.delegateRenderer.removeChild(parent, oldChild, isHostElement, requireSynchronousElementRemoval);
|
|
2332
2562
|
}
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
// disassociate oldChild from parent children
|
|
2336
|
-
pRS[6 /* NgtRendererClassId.children */].splice(childIndex, 1);
|
|
2563
|
+
if (cRS[0 /* NgtRendererClassId.type */] !== 'three') {
|
|
2564
|
+
this.detachThreeDescendants(oldChild);
|
|
2337
2565
|
}
|
|
2338
|
-
|
|
2339
|
-
|
|
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;
|
|
2340
2574
|
}
|
|
2341
2575
|
if (pRS[0 /* NgtRendererClassId.type */] === 'platform' && cRS[0 /* NgtRendererClassId.type */] === 'platform') {
|
|
2342
|
-
return this.delegateRenderer.removeChild(parent, oldChild, isHostElement);
|
|
2576
|
+
return this.delegateRenderer.removeChild(parent, oldChild, isHostElement, requireSynchronousElementRemoval);
|
|
2343
2577
|
}
|
|
2344
|
-
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')) {
|
|
2345
2582
|
return;
|
|
2346
2583
|
}
|
|
2347
|
-
|
|
2348
|
-
const childLS = getInstanceState(oldChild);
|
|
2349
|
-
if (!childLS)
|
|
2350
|
-
return;
|
|
2351
|
-
const threeParent = childLS.parent ? untracked(childLS.parent) : null;
|
|
2352
|
-
if (!threeParent)
|
|
2353
|
-
return;
|
|
2354
|
-
return this.removeChild(threeParent, oldChild);
|
|
2355
|
-
}
|
|
2356
|
-
return this.delegateRenderer.removeChild(parent, oldChild, isHostElement);
|
|
2584
|
+
return this.delegateRenderer.removeChild(parent, oldChild, isHostElement, requireSynchronousElementRemoval);
|
|
2357
2585
|
}
|
|
2358
2586
|
parentNode(node) {
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
isRendererNode(node)
|
|
2363
|
-
|
|
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;
|
|
2364
2593
|
// this should not happen but if it does, we'll delegate to the renderer
|
|
2365
2594
|
if (!store) {
|
|
2366
2595
|
return this.delegateRenderer.parentNode(node);
|
|
@@ -2370,24 +2599,18 @@ class NgtRenderer2 {
|
|
|
2370
2599
|
if (!rootScene) {
|
|
2371
2600
|
return this.delegateRenderer.parentNode(node);
|
|
2372
2601
|
}
|
|
2373
|
-
// if root scene is not a renderer node,
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
if (node[NGT_PORTAL_CONTENT_FLAG] &&
|
|
2380
|
-
node[NGT_DOM_PARENT_FLAG] &&
|
|
2381
|
-
isRendererNode(node[NGT_DOM_PARENT_FLAG])) {
|
|
2382
|
-
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;
|
|
2383
2608
|
const portalContentParentRS = portalContentParent.__ngt_renderer__;
|
|
2384
|
-
|
|
2385
|
-
portalContentParentRS[3 /* NgtRendererClassId.portalContainer */] = rootScene;
|
|
2386
|
-
}
|
|
2609
|
+
portalContentParentRS[3 /* NgtRendererClassId.portalContainer */] = rendererRootScene;
|
|
2387
2610
|
}
|
|
2388
|
-
return
|
|
2611
|
+
return rendererRootScene;
|
|
2389
2612
|
}
|
|
2390
|
-
const rendererParentNode = node.__ngt_renderer__?.[
|
|
2613
|
+
const rendererParentNode = node.__ngt_renderer__?.[4 /* NgtRendererClassId.parent */];
|
|
2391
2614
|
// returns the renderer parent node if it exists, otherwise returns the delegateRenderer parentNode
|
|
2392
2615
|
return rendererParentNode ?? this.delegateRenderer.parentNode(node);
|
|
2393
2616
|
}
|
|
@@ -2396,6 +2619,15 @@ class NgtRenderer2 {
|
|
|
2396
2619
|
if (!rS || rS[1 /* NgtRendererClassId.destroyed */])
|
|
2397
2620
|
return this.delegateRenderer.removeAttribute(el, name, namespace);
|
|
2398
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());
|
|
2399
2631
|
return;
|
|
2400
2632
|
}
|
|
2401
2633
|
return this.delegateRenderer.removeAttribute(el, name, namespace);
|
|
@@ -2411,12 +2643,9 @@ class NgtRenderer2 {
|
|
|
2411
2643
|
}
|
|
2412
2644
|
if (rS[0 /* NgtRendererClassId.type */] === 'three') {
|
|
2413
2645
|
if (name === 'attach') {
|
|
2414
|
-
const
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
if (instanceState)
|
|
2418
|
-
instanceState.attach = paths;
|
|
2419
|
-
}
|
|
2646
|
+
const instanceState = getInstanceState(el);
|
|
2647
|
+
const parent = (instanceState?.parent ? untracked(instanceState.parent) : null) ?? this.findTargetThreeParent(el);
|
|
2648
|
+
this.updateAttachment(el, value, parent);
|
|
2420
2649
|
return;
|
|
2421
2650
|
}
|
|
2422
2651
|
// coercion for primitive values
|
|
@@ -2443,44 +2672,55 @@ class NgtRenderer2 {
|
|
|
2443
2672
|
// NOTE: untrack all signal updates because this is during setProperty which is a reactive context
|
|
2444
2673
|
// attaching potentially updates signals which is not allowed
|
|
2445
2674
|
const rS = el.__ngt_renderer__;
|
|
2446
|
-
if (!rS
|
|
2675
|
+
if (!rS)
|
|
2676
|
+
return this.delegateRenderer.setProperty(el, name, value);
|
|
2677
|
+
if (rS[1 /* NgtRendererClassId.destroyed */]) {
|
|
2447
2678
|
this.options.verbose &&
|
|
2448
2679
|
console.warn('[NGT dev mode] setProperty is invoked on destroyed renderer node.', { el, name, value });
|
|
2449
2680
|
return;
|
|
2450
2681
|
}
|
|
2451
|
-
if (
|
|
2682
|
+
if (isRendererNodeType(el, 'three')) {
|
|
2683
|
+
const threeState = el.__ngt_renderer__;
|
|
2452
2684
|
const instanceState = getInstanceState(el);
|
|
2453
|
-
const
|
|
2685
|
+
const attachedParent = instanceState?.hierarchyStore.snapshot.parent ?? null;
|
|
2686
|
+
const parent = attachedParent ?? this.findTargetThreeParent(el);
|
|
2454
2687
|
if (name === 'parameters') {
|
|
2688
|
+
const parameters = value && typeof value === 'object' ? { ...value } : {};
|
|
2455
2689
|
// NOTE: short-cut for null raycast to prevent upstream from creating a nullRaycast property
|
|
2456
|
-
if ('raycast' in
|
|
2457
|
-
|
|
2690
|
+
if ('raycast' in parameters && parameters['raycast'] === null) {
|
|
2691
|
+
parameters['raycast'] = () => null;
|
|
2458
2692
|
}
|
|
2459
|
-
|
|
2460
|
-
|
|
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'))) {
|
|
2461
2702
|
untracked(() => instanceState?.updateGeometryStamp());
|
|
2462
2703
|
}
|
|
2463
|
-
if ('attach' in
|
|
2464
|
-
|
|
2465
|
-
instanceState.attach = this.normalizeAttach(value['attach']);
|
|
2466
|
-
if (parent)
|
|
2467
|
-
untracked(() => attachThreeNodes(parent, el));
|
|
2704
|
+
if ('attach' in parameters || previousKeys.has('attach')) {
|
|
2705
|
+
this.updateAttachment(el, nextAttach, parent);
|
|
2468
2706
|
}
|
|
2469
2707
|
return;
|
|
2470
2708
|
}
|
|
2471
2709
|
// [rawValue]
|
|
2472
2710
|
if (instanceState?.type === 'ngt-value' && name === 'rawValue') {
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
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
|
+
});
|
|
2476
2719
|
return;
|
|
2477
2720
|
}
|
|
2478
2721
|
// [attach]
|
|
2479
2722
|
if (name === 'attach') {
|
|
2480
|
-
|
|
2481
|
-
instanceState.attach = this.normalizeAttach(value);
|
|
2482
|
-
if (parent)
|
|
2483
|
-
untracked(() => attachThreeNodes(parent, el));
|
|
2723
|
+
this.updateAttachment(el, value, parent);
|
|
2484
2724
|
return;
|
|
2485
2725
|
}
|
|
2486
2726
|
// NOTE: short-cut for null raycast to prevent upstream from creating a nullRaycast property
|
|
@@ -2488,7 +2728,7 @@ class NgtRenderer2 {
|
|
|
2488
2728
|
value = () => null;
|
|
2489
2729
|
}
|
|
2490
2730
|
applyProps(el, { [name]: value });
|
|
2491
|
-
if (instanceState && name === 'geometry'
|
|
2731
|
+
if (instanceState && name === 'geometry') {
|
|
2492
2732
|
untracked(() => {
|
|
2493
2733
|
instanceState.updateGeometryStamp();
|
|
2494
2734
|
});
|
|
@@ -2497,13 +2737,13 @@ class NgtRenderer2 {
|
|
|
2497
2737
|
}
|
|
2498
2738
|
return this.delegateRenderer.setProperty(el, name, value);
|
|
2499
2739
|
}
|
|
2500
|
-
listen(target, eventName, callback) {
|
|
2740
|
+
listen(target, eventName, callback, options) {
|
|
2501
2741
|
if (typeof target === 'string') {
|
|
2502
|
-
return this.delegateRenderer.listen(target, eventName, callback);
|
|
2742
|
+
return this.delegateRenderer.listen(target, eventName, callback, options);
|
|
2503
2743
|
}
|
|
2504
2744
|
const rS = target.__ngt_renderer__;
|
|
2505
2745
|
if (!rS) {
|
|
2506
|
-
return this.delegateRenderer.listen(target, eventName, callback);
|
|
2746
|
+
return this.delegateRenderer.listen(target, eventName, callback, options);
|
|
2507
2747
|
}
|
|
2508
2748
|
if (rS[1 /* NgtRendererClassId.destroyed */])
|
|
2509
2749
|
return () => { };
|
|
@@ -2518,43 +2758,34 @@ class NgtRenderer2 {
|
|
|
2518
2758
|
return () => { };
|
|
2519
2759
|
}
|
|
2520
2760
|
if (eventName === 'attached') {
|
|
2521
|
-
|
|
2522
|
-
const parent = iS.parent && untracked(iS.parent);
|
|
2523
|
-
if (parent)
|
|
2524
|
-
iS.onAttach({ parent, node: target });
|
|
2525
|
-
return () => {
|
|
2526
|
-
iS.onAttach = undefined;
|
|
2527
|
-
};
|
|
2761
|
+
return this.registerWithOptions(callback, options, (listener) => this.listenToInstanceEvent(target, 'attached', listener));
|
|
2528
2762
|
}
|
|
2529
2763
|
if (eventName === 'updated') {
|
|
2530
|
-
|
|
2531
|
-
return () => {
|
|
2532
|
-
iS.onUpdate = undefined;
|
|
2533
|
-
};
|
|
2764
|
+
return this.registerWithOptions(callback, options, (listener) => this.listenToInstanceEvent(target, 'updated', listener));
|
|
2534
2765
|
}
|
|
2535
2766
|
if (THREE_NATIVE_EVENTS.includes(eventName) && target instanceof THREE.EventDispatcher) {
|
|
2536
2767
|
// NOTE: rename to dispose because that's the event type, not disposed.
|
|
2537
2768
|
if (eventName === 'disposed') {
|
|
2538
2769
|
eventName = 'dispose';
|
|
2539
2770
|
}
|
|
2540
|
-
|
|
2541
|
-
(eventName
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
};
|
|
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
|
+
});
|
|
2548
2778
|
}
|
|
2549
|
-
const cleanup = iS.setPointerEvent?.(eventName,
|
|
2779
|
+
const cleanup = this.registerWithOptions(callback, options, (listener) => iS.setPointerEvent?.(eventName, listener) || (() => { }));
|
|
2550
2780
|
// this means the object has already been attached to the parent and has its store propagated
|
|
2551
2781
|
if (iS.store)
|
|
2552
2782
|
iS.addInteraction?.(iS.store);
|
|
2553
2783
|
return cleanup;
|
|
2554
2784
|
}
|
|
2555
|
-
return this.delegateRenderer.listen(target, eventName, callback);
|
|
2785
|
+
return this.delegateRenderer.listen(target, eventName, callback, options);
|
|
2556
2786
|
}
|
|
2557
|
-
appendThreeRendererNodes(parent, child) {
|
|
2787
|
+
appendThreeRendererNodes(parent, child, refChild, storeOverride) {
|
|
2788
|
+
parent = child.__ngt_renderer__[6 /* NgtRendererClassId.parentOverride */] ?? parent;
|
|
2558
2789
|
// if parent and child are the same, skip
|
|
2559
2790
|
if (parent === child) {
|
|
2560
2791
|
this.options.verbose &&
|
|
@@ -2564,31 +2795,226 @@ class NgtRenderer2 {
|
|
|
2564
2795
|
});
|
|
2565
2796
|
return;
|
|
2566
2797
|
}
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
});
|
|
2575
|
-
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);
|
|
2576
2805
|
}
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
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);
|
|
2581
2810
|
return;
|
|
2582
2811
|
}
|
|
2583
|
-
setNodeRelationship(parent, child) {
|
|
2584
|
-
|
|
2585
|
-
|
|
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
|
+
};
|
|
2586
2984
|
}
|
|
2587
|
-
|
|
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) {
|
|
2588
3014
|
let directiveInstance;
|
|
2589
|
-
let i =
|
|
3015
|
+
let i = this.directiveInjectors.length - 1;
|
|
2590
3016
|
while (i >= 0) {
|
|
2591
|
-
const injector =
|
|
3017
|
+
const injector = this.directiveInjectors[i];
|
|
2592
3018
|
const instance = injector.get(directive, null);
|
|
2593
3019
|
if (instance && typeof instance === 'object' && instance.validate()) {
|
|
2594
3020
|
directiveInstance = instance;
|
|
@@ -2599,12 +3025,75 @@ class NgtRenderer2 {
|
|
|
2599
3025
|
return directiveInstance;
|
|
2600
3026
|
}
|
|
2601
3027
|
normalizeAttach(attach) {
|
|
3028
|
+
if (attach == null)
|
|
3029
|
+
return undefined;
|
|
2602
3030
|
if (typeof attach === 'function')
|
|
2603
3031
|
return attach;
|
|
2604
3032
|
if (typeof attach === 'string')
|
|
2605
3033
|
return attach.split('.');
|
|
2606
3034
|
return attach.flatMap((item) => item.toString().split('.'));
|
|
2607
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
|
+
}
|
|
2608
3097
|
}
|
|
2609
3098
|
|
|
2610
3099
|
/**
|
|
@@ -2633,6 +3122,12 @@ function storeFactory() {
|
|
|
2633
3122
|
const defaultTarget = new THREE.Vector3();
|
|
2634
3123
|
const tempTarget = new THREE.Vector3();
|
|
2635
3124
|
let performanceTimeout = undefined;
|
|
3125
|
+
const cancelPerformanceRegression = () => {
|
|
3126
|
+
if (performanceTimeout === undefined)
|
|
3127
|
+
return;
|
|
3128
|
+
clearTimeout(performanceTimeout);
|
|
3129
|
+
performanceTimeout = undefined;
|
|
3130
|
+
};
|
|
2636
3131
|
const pointer = new THREE.Vector2();
|
|
2637
3132
|
// getCurrentViewport will mutate this instead of creating a new object everytime
|
|
2638
3133
|
const tempViewport = {
|
|
@@ -2672,17 +3167,19 @@ function storeFactory() {
|
|
|
2672
3167
|
regress: () => {
|
|
2673
3168
|
const state = store.snapshot;
|
|
2674
3169
|
// Clear timeout
|
|
2675
|
-
|
|
2676
|
-
clearTimeout(performanceTimeout);
|
|
3170
|
+
cancelPerformanceRegression();
|
|
2677
3171
|
// Set lower bound performance
|
|
2678
3172
|
if (state.performance.current !== state.performance.min)
|
|
2679
3173
|
store.update((state) => ({
|
|
2680
3174
|
performance: { ...state.performance, current: state.performance.min },
|
|
2681
3175
|
}));
|
|
2682
3176
|
// Go back to upper bound performance after a while unless something regresses meanwhile
|
|
2683
|
-
performanceTimeout = setTimeout(() =>
|
|
2684
|
-
|
|
2685
|
-
|
|
3177
|
+
performanceTimeout = setTimeout(() => {
|
|
3178
|
+
performanceTimeout = undefined;
|
|
3179
|
+
store.update((state) => ({
|
|
3180
|
+
performance: { ...state.performance, current: store.snapshot.performance.max },
|
|
3181
|
+
}));
|
|
3182
|
+
}, state.performance.debounce);
|
|
2686
3183
|
},
|
|
2687
3184
|
},
|
|
2688
3185
|
size: { width: 0, height: 0, top: 0, left: 0 },
|
|
@@ -2770,27 +3267,34 @@ function storeFactory() {
|
|
|
2770
3267
|
subscribers: [],
|
|
2771
3268
|
subscribe: (callback, priority = 0, _store = store) => {
|
|
2772
3269
|
const internal = _store.snapshot.internal;
|
|
3270
|
+
const subscription = { callback, priority, store: _store };
|
|
2773
3271
|
// If this subscription was given a priority, it takes rendering into its own hands
|
|
2774
3272
|
// For that reason we switch off automatic rendering and increase the manual flag
|
|
2775
3273
|
// As long as this flag is positive there can be no internal rendering at all
|
|
2776
3274
|
// because there could be multiple render subscriptions
|
|
2777
|
-
internal.priority
|
|
2778
|
-
internal.subscribers.push({ callback, priority, store: _store });
|
|
3275
|
+
internal.priority += priority > 0 ? 1 : 0;
|
|
2779
3276
|
// Register subscriber and sort layers from lowest to highest, meaning,
|
|
2780
3277
|
// highest priority renders last (on top of the other frames)
|
|
2781
|
-
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;
|
|
2782
3280
|
return () => {
|
|
3281
|
+
if (!active)
|
|
3282
|
+
return;
|
|
3283
|
+
active = false;
|
|
2783
3284
|
const internal = _store.snapshot.internal;
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
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);
|
|
2790
3291
|
};
|
|
2791
3292
|
},
|
|
2792
3293
|
},
|
|
2793
3294
|
});
|
|
3295
|
+
Object.defineProperty(store.snapshot.performance.regress, 'cancel', {
|
|
3296
|
+
value: cancelPerformanceRegression,
|
|
3297
|
+
});
|
|
2794
3298
|
Object.defineProperty(store, '__pointerMissed$', { get: () => pointerMissed$ });
|
|
2795
3299
|
let { size: oldSize, viewport: { dpr: oldDpr }, camera: oldCamera, } = store.snapshot;
|
|
2796
3300
|
effect(() => {
|
|
@@ -2904,22 +3408,11 @@ class NgtParent extends NgtCommonDirective {
|
|
|
2904
3408
|
this.linkedValue = linkedSignal(this._parent, ...(ngDevMode ? [{ debugName: "linkedValue" }] : /* istanbul ignore next */ []));
|
|
2905
3409
|
this.shouldSkipRender = computed(() => !this._parent(), ...(ngDevMode ? [{ debugName: "shouldSkipRender" }] : /* istanbul ignore next */ []));
|
|
2906
3410
|
const commentNode = this.commentNode;
|
|
2907
|
-
commentNode.
|
|
2908
|
-
commentNode[NGT_PARENT_FLAG] = true;
|
|
2909
|
-
if (commentNode[NGT_INTERNAL_ADD_COMMENT_FLAG]) {
|
|
2910
|
-
commentNode[NGT_INTERNAL_ADD_COMMENT_FLAG]('parent', this.injector);
|
|
2911
|
-
delete commentNode[NGT_INTERNAL_ADD_COMMENT_FLAG];
|
|
2912
|
-
}
|
|
3411
|
+
setRendererAnchor(commentNode, { kind: 'parent', injector: this.injector });
|
|
2913
3412
|
}
|
|
2914
3413
|
validate() {
|
|
2915
3414
|
return !this.injected && !!this.injectedValue;
|
|
2916
3415
|
}
|
|
2917
|
-
beforeCreateView() {
|
|
2918
|
-
const commentNode = this.commentNode;
|
|
2919
|
-
if (commentNode[NGT_INTERNAL_SET_PARENT_COMMENT_FLAG]) {
|
|
2920
|
-
commentNode[NGT_INTERNAL_SET_PARENT_COMMENT_FLAG](this.injectedValue);
|
|
2921
|
-
}
|
|
2922
|
-
}
|
|
2923
3416
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: NgtParent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2924
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 }); }
|
|
2925
3418
|
}
|
|
@@ -3085,8 +3578,135 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
3085
3578
|
type: Directive
|
|
3086
3579
|
}], ctorParameters: () => [] });
|
|
3087
3580
|
|
|
3088
|
-
const
|
|
3089
|
-
|
|
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();
|
|
3090
3710
|
function normalizeInputs$1(input) {
|
|
3091
3711
|
let urls = [];
|
|
3092
3712
|
if (Array.isArray(input)) {
|
|
@@ -3100,33 +3720,26 @@ function normalizeInputs$1(input) {
|
|
|
3100
3720
|
}
|
|
3101
3721
|
return urls.map((url) => (url.includes('undefined') || url.includes('null') || !url ? '' : url));
|
|
3102
3722
|
}
|
|
3103
|
-
function load(loaderConstructorFactory, inputs, { extensions, onLoad, onProgress, } = {}) {
|
|
3104
|
-
return () => {
|
|
3723
|
+
function load(loaderConstructorFactory, inputs, { extensions, cacheKey, onLoad, onProgress, } = {}) {
|
|
3724
|
+
return (consumerOnLoad = onLoad) => {
|
|
3105
3725
|
const urls = normalizeInputs$1(inputs());
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
loader = new (loaderConstructorFactory(urls))();
|
|
3109
|
-
memoizedLoaders$1.set(loaderConstructorFactory(urls), loader);
|
|
3110
|
-
}
|
|
3111
|
-
if (extensions)
|
|
3112
|
-
extensions(loader);
|
|
3726
|
+
const LoaderConstructor = loaderConstructorFactory(urls);
|
|
3727
|
+
const configurationKey = loaderCache$1.configurationKey(extensions, cacheKey?.());
|
|
3113
3728
|
return urls.map((url) => {
|
|
3114
3729
|
if (url === '')
|
|
3115
3730
|
return Promise.resolve(null);
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
}
|
|
3129
|
-
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
|
+
});
|
|
3130
3743
|
});
|
|
3131
3744
|
};
|
|
3132
3745
|
}
|
|
@@ -3134,18 +3747,30 @@ function load(loaderConstructorFactory, inputs, { extensions, onLoad, onProgress
|
|
|
3134
3747
|
* @deprecated Use loaderResource instead. Will be removed in v5.0.0
|
|
3135
3748
|
* @since v4.0.0~
|
|
3136
3749
|
*/
|
|
3137
|
-
function _injectLoader(loaderConstructorFactory, inputs, { extensions, onProgress, onLoad, injector, } = {}) {
|
|
3750
|
+
function _injectLoader(loaderConstructorFactory, inputs, { extensions, cacheKey, onProgress, onLoad, injector, } = {}) {
|
|
3138
3751
|
return assertInjector(_injectLoader, injector, () => {
|
|
3139
3752
|
const response = signal(null, ...(ngDevMode ? [{ debugName: "response" }] : /* istanbul ignore next */ []));
|
|
3140
3753
|
const cachedResultPromisesEffect = load(loaderConstructorFactory, inputs, {
|
|
3141
3754
|
extensions,
|
|
3755
|
+
cacheKey,
|
|
3142
3756
|
onProgress,
|
|
3143
|
-
onLoad: onLoad,
|
|
3144
3757
|
});
|
|
3145
|
-
effect(() => {
|
|
3758
|
+
effect((onCleanup) => {
|
|
3759
|
+
let active = true;
|
|
3760
|
+
onCleanup(() => {
|
|
3761
|
+
active = false;
|
|
3762
|
+
});
|
|
3146
3763
|
const originalUrls = inputs();
|
|
3147
|
-
const cachedResultPromises = cachedResultPromisesEffect()
|
|
3148
|
-
|
|
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;
|
|
3149
3774
|
response.update(() => {
|
|
3150
3775
|
if (Array.isArray(originalUrls))
|
|
3151
3776
|
return results;
|
|
@@ -3158,25 +3783,23 @@ function _injectLoader(loaderConstructorFactory, inputs, { extensions, onProgres
|
|
|
3158
3783
|
return result;
|
|
3159
3784
|
}, {});
|
|
3160
3785
|
});
|
|
3161
|
-
})
|
|
3786
|
+
})
|
|
3787
|
+
.catch(() => undefined);
|
|
3162
3788
|
});
|
|
3163
3789
|
return response.asReadonly();
|
|
3164
3790
|
});
|
|
3165
3791
|
}
|
|
3166
|
-
_injectLoader.preload = (loaderConstructorFactory, inputs, extensions, onLoad) => {
|
|
3167
|
-
const effects = load(loaderConstructorFactory, inputs, { extensions, onLoad })();
|
|
3792
|
+
_injectLoader.preload = (loaderConstructorFactory, inputs, extensions, onLoad, cacheKey) => {
|
|
3793
|
+
const effects = load(loaderConstructorFactory, inputs, { extensions, cacheKey, onLoad })();
|
|
3168
3794
|
if (effects) {
|
|
3169
|
-
void Promise.all(effects);
|
|
3795
|
+
void Promise.all(effects).catch(() => undefined);
|
|
3170
3796
|
}
|
|
3171
3797
|
};
|
|
3172
3798
|
_injectLoader.destroy = () => {
|
|
3173
|
-
|
|
3799
|
+
loaderCache$1.destroy();
|
|
3174
3800
|
};
|
|
3175
3801
|
_injectLoader.clear = (urls) => {
|
|
3176
|
-
|
|
3177
|
-
urlToClear.forEach((url) => {
|
|
3178
|
-
cached$1.delete(url);
|
|
3179
|
-
});
|
|
3802
|
+
loaderCache$1.clear(urls);
|
|
3180
3803
|
};
|
|
3181
3804
|
/**
|
|
3182
3805
|
* @deprecated Use loaderResource instead. Will be removed in v5.0.0
|
|
@@ -3204,38 +3827,26 @@ function normalizeInputs(input) {
|
|
|
3204
3827
|
}
|
|
3205
3828
|
return urls.map((url) => (url.includes('undefined') || url.includes('null') || !url ? '' : url));
|
|
3206
3829
|
}
|
|
3207
|
-
const
|
|
3208
|
-
|
|
3209
|
-
function getLoaderResourceParams(input, loaderConstructorFactory, extensions) {
|
|
3830
|
+
const loaderCache = new NgtLoaderCache();
|
|
3831
|
+
function getLoaderResourceParams(input, loaderConstructorFactory, extensions, cacheKey) {
|
|
3210
3832
|
const urls = input();
|
|
3211
3833
|
const LoaderConstructor = loaderConstructorFactory(urls);
|
|
3212
3834
|
const normalizedUrls = normalizeInputs(urls);
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
loader = new LoaderConstructor();
|
|
3216
|
-
memoizedLoaders.set(LoaderConstructor, loader);
|
|
3217
|
-
}
|
|
3218
|
-
if (extensions)
|
|
3219
|
-
extensions(loader);
|
|
3220
|
-
return { urls, normalizedUrls, loader };
|
|
3835
|
+
const configurationKey = loaderCache.configurationKey(extensions, cacheKey?.());
|
|
3836
|
+
return { urls, normalizedUrls, LoaderConstructor, configurationKey, extensions };
|
|
3221
3837
|
}
|
|
3222
3838
|
function getLoaderPromises(params, onProgress) {
|
|
3223
3839
|
return params.normalizedUrls.map((url) => {
|
|
3224
3840
|
if (url === '')
|
|
3225
3841
|
return Promise.resolve(null);
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
return cachedPromise;
|
|
3229
|
-
const promise = new Promise((res, rej) => {
|
|
3230
|
-
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) => {
|
|
3231
3844
|
if ('scene' in data) {
|
|
3232
3845
|
Object.assign(data, makeObjectGraph(data['scene']));
|
|
3233
3846
|
}
|
|
3234
3847
|
res(data);
|
|
3235
3848
|
}, onProgress, (error) => rej(new Error(`[NGT] Could not load ${url}: ${error?.message}`)));
|
|
3236
|
-
});
|
|
3237
|
-
cached.set(url, promise);
|
|
3238
|
-
return promise;
|
|
3849
|
+
}));
|
|
3239
3850
|
});
|
|
3240
3851
|
}
|
|
3241
3852
|
/**
|
|
@@ -3275,11 +3886,11 @@ function getLoaderPromises(params, onProgress) {
|
|
|
3275
3886
|
* }
|
|
3276
3887
|
* ```
|
|
3277
3888
|
*/
|
|
3278
|
-
function loaderResource(loaderConstructorFactory, input, { extensions, onLoad, onProgress, injector, } = {}) {
|
|
3889
|
+
function loaderResource(loaderConstructorFactory, input, { extensions, cacheKey, onLoad, onProgress, injector, } = {}) {
|
|
3279
3890
|
return assertInjector(loaderResource, injector, () => {
|
|
3280
3891
|
return resource({
|
|
3281
|
-
params: () => getLoaderResourceParams(input, loaderConstructorFactory, extensions),
|
|
3282
|
-
loader: async ({ params }) => {
|
|
3892
|
+
params: () => getLoaderResourceParams(input, loaderConstructorFactory, extensions, cacheKey),
|
|
3893
|
+
loader: async ({ params, abortSignal }) => {
|
|
3283
3894
|
// TODO: use the abortSignal when THREE.Loader supports it
|
|
3284
3895
|
const loadedResults = await Promise.all(getLoaderPromises(params, onProgress));
|
|
3285
3896
|
let results;
|
|
@@ -3297,25 +3908,22 @@ function loaderResource(loaderConstructorFactory, input, { extensions, onLoad, o
|
|
|
3297
3908
|
return result;
|
|
3298
3909
|
}, {});
|
|
3299
3910
|
}
|
|
3300
|
-
if (
|
|
3301
|
-
onLoad(results);
|
|
3911
|
+
if (!abortSignal.aborted)
|
|
3912
|
+
onLoad?.(results);
|
|
3302
3913
|
return results;
|
|
3303
3914
|
},
|
|
3304
3915
|
});
|
|
3305
3916
|
});
|
|
3306
3917
|
}
|
|
3307
|
-
loaderResource.preload = (loaderConstructor, inputs, extensions) => {
|
|
3308
|
-
const params = getLoaderResourceParams(() => inputs, () => loaderConstructor, extensions);
|
|
3309
|
-
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);
|
|
3310
3921
|
};
|
|
3311
3922
|
loaderResource.destroy = () => {
|
|
3312
|
-
|
|
3923
|
+
loaderCache.destroy();
|
|
3313
3924
|
};
|
|
3314
3925
|
loaderResource.clear = (urls) => {
|
|
3315
|
-
|
|
3316
|
-
urlToClear.forEach((url) => {
|
|
3317
|
-
cached.delete(url);
|
|
3318
|
-
});
|
|
3926
|
+
loaderCache.clear(urls);
|
|
3319
3927
|
};
|
|
3320
3928
|
|
|
3321
3929
|
const RGBA_REGEX = /rgba?\((\d+),\s*(\d+),\s*(\d+),?\s*(\d*\.?\d+)?\)/;
|
|
@@ -3639,7 +4247,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
3639
4247
|
* ```
|
|
3640
4248
|
*/
|
|
3641
4249
|
class NgtPortalContent {
|
|
3642
|
-
static ngTemplateContextGuard(_,
|
|
4250
|
+
static ngTemplateContextGuard(_, _ctx) {
|
|
3643
4251
|
return true;
|
|
3644
4252
|
}
|
|
3645
4253
|
constructor() {
|
|
@@ -3647,9 +4255,10 @@ class NgtPortalContent {
|
|
|
3647
4255
|
const { element } = inject(ViewContainerRef);
|
|
3648
4256
|
const commentNode = element.nativeElement;
|
|
3649
4257
|
const store = injectStore();
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
commentNode
|
|
4258
|
+
const domParent = host.nativeElement;
|
|
4259
|
+
const anchor = { kind: 'portal', store, domParent };
|
|
4260
|
+
setRendererAnchor(commentNode, anchor);
|
|
4261
|
+
setRendererAnchor(domParent, anchor);
|
|
3653
4262
|
}
|
|
3654
4263
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: NgtPortalContent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
3655
4264
|
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: NgtPortalContent, isStandalone: true, selector: "ng-template[portalContent]", ngImport: i0 }); }
|
|
@@ -3658,34 +4267,115 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
3658
4267
|
type: Directive,
|
|
3659
4268
|
args: [{ selector: 'ng-template[portalContent]' }]
|
|
3660
4269
|
}], ctorParameters: () => [] });
|
|
3661
|
-
function mergeState(previousRoot, store, container,
|
|
3662
|
-
//
|
|
3663
|
-
|
|
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;
|
|
3664
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 };
|
|
3665
4279
|
let viewport = undefined;
|
|
3666
|
-
if (
|
|
3667
|
-
const camera = state.camera;
|
|
4280
|
+
if (camera && (camera !== previousState.camera || size)) {
|
|
3668
4281
|
// calculate the override viewport, if present
|
|
3669
|
-
viewport = previousState.viewport.getCurrentViewport(camera, new THREE.Vector3(),
|
|
3670
|
-
//
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
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,
|
|
3678
4296
|
// portals have their own scene, which forms the root, a raycaster and a pointer
|
|
3679
4297
|
scene: container,
|
|
3680
|
-
pointer,
|
|
3681
|
-
raycaster,
|
|
4298
|
+
pointer: runtime.pointer,
|
|
4299
|
+
raycaster: runtime.raycaster,
|
|
3682
4300
|
// their previous root is the layer before it
|
|
3683
4301
|
previousRoot,
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
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);
|
|
3689
4379
|
};
|
|
3690
4380
|
}
|
|
3691
4381
|
/**
|
|
@@ -3709,6 +4399,16 @@ function mergeState(previousRoot, store, container, pointer, raycaster, events,
|
|
|
3709
4399
|
* ```
|
|
3710
4400
|
*/
|
|
3711
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
|
+
}
|
|
3712
4412
|
constructor() {
|
|
3713
4413
|
this.container = input.required(...(ngDevMode ? [{ debugName: "container" }] : /* istanbul ignore next */ []));
|
|
3714
4414
|
this.state = input({}, ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
|
|
@@ -3717,6 +4417,7 @@ class NgtPortalImpl {
|
|
|
3717
4417
|
this.previousStore = injectStore({ skipSelf: true });
|
|
3718
4418
|
this.portalStore = injectStore();
|
|
3719
4419
|
this.injector = inject(Injector);
|
|
4420
|
+
this.document = inject(DOCUMENT);
|
|
3720
4421
|
this.size = pick(this.state, 'size');
|
|
3721
4422
|
this.events = pick(this.state, 'events');
|
|
3722
4423
|
this.restState = omit(this.state, ['size', 'events']);
|
|
@@ -3724,21 +4425,42 @@ class NgtPortalImpl {
|
|
|
3724
4425
|
this.portalRendered = this.portalContentRendered.asReadonly();
|
|
3725
4426
|
extend({ Group });
|
|
3726
4427
|
effect(() => {
|
|
3727
|
-
|
|
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] = [
|
|
3728
4434
|
this.container(),
|
|
3729
4435
|
this.anchorRef(),
|
|
3730
4436
|
this.contentRef(),
|
|
3731
4437
|
this.previousStore(),
|
|
4438
|
+
this.size(),
|
|
4439
|
+
this.events(),
|
|
4440
|
+
this.restState(),
|
|
3732
4441
|
];
|
|
3733
|
-
const
|
|
3734
|
-
if (!
|
|
3735
|
-
|
|
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;
|
|
3736
4454
|
}
|
|
3737
4455
|
const instanceState = getInstanceState(container);
|
|
3738
|
-
if (instanceState &&
|
|
3739
|
-
|
|
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 };
|
|
3740
4462
|
}
|
|
3741
|
-
this.portalStore.update(
|
|
4463
|
+
this.portalStore.update(mergeState(this.previousStore, this.portalStore, previousState, container, runtime, restState, events, size));
|
|
3742
4464
|
if (this.portalViewRef) {
|
|
3743
4465
|
this.portalViewRef.detectChanges();
|
|
3744
4466
|
return;
|
|
@@ -3748,6 +4470,12 @@ class NgtPortalImpl {
|
|
|
3748
4470
|
this.portalViewRef.detectChanges();
|
|
3749
4471
|
this.portalContentRendered.set(true);
|
|
3750
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
|
+
});
|
|
3751
4479
|
}
|
|
3752
4480
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: NgtPortalImpl, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3753
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: [
|
|
@@ -3765,7 +4493,32 @@ class NgtPortalImpl {
|
|
|
3765
4493
|
pointer,
|
|
3766
4494
|
raycaster,
|
|
3767
4495
|
});
|
|
3768
|
-
|
|
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, {}));
|
|
3769
4522
|
return store;
|
|
3770
4523
|
},
|
|
3771
4524
|
deps: [[new SkipSelf(), NGT_STORE]],
|
|
@@ -3804,7 +4557,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
3804
4557
|
pointer,
|
|
3805
4558
|
raycaster,
|
|
3806
4559
|
});
|
|
3807
|
-
|
|
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, {}));
|
|
3808
4586
|
return store;
|
|
3809
4587
|
},
|
|
3810
4588
|
deps: [[new SkipSelf(), NGT_STORE]],
|
|
@@ -3826,6 +4604,95 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
3826
4604
|
const NgtPortal = [NgtPortalImpl, NgtPortalContent];
|
|
3827
4605
|
|
|
3828
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
|
+
}
|
|
3829
4696
|
/**
|
|
3830
4697
|
* Creates a canvas root initializer function.
|
|
3831
4698
|
*
|
|
@@ -3847,10 +4714,12 @@ function canvasRootInitializer(injector) {
|
|
|
3847
4714
|
return assertInjector(canvasRootInitializer, injector, () => {
|
|
3848
4715
|
const injectedStore = injectStore();
|
|
3849
4716
|
const loop = injectLoop();
|
|
4717
|
+
const rootInjector = inject(Injector);
|
|
3850
4718
|
return (canvas) => {
|
|
3851
4719
|
const exist = roots.has(canvas);
|
|
3852
4720
|
let store = roots.get(canvas);
|
|
3853
|
-
|
|
4721
|
+
const previousLifecycle = rootLifecycles.get(canvas);
|
|
4722
|
+
if (store && !previousLifecycle?.teardown) {
|
|
3854
4723
|
console.warn('[NGT] Same canvas root is being created twice');
|
|
3855
4724
|
}
|
|
3856
4725
|
store ||= injectedStore;
|
|
@@ -3860,33 +4729,95 @@ function canvasRootInitializer(injector) {
|
|
|
3860
4729
|
if (!exist) {
|
|
3861
4730
|
roots.set(canvas, store);
|
|
3862
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 });
|
|
3863
4762
|
let isConfigured = false;
|
|
3864
|
-
let
|
|
4763
|
+
let destroyRequested = false;
|
|
3865
4764
|
return {
|
|
3866
|
-
isConfigured
|
|
4765
|
+
get isConfigured() {
|
|
4766
|
+
return isConfigured;
|
|
4767
|
+
},
|
|
3867
4768
|
destroy: (timeout = 500) => {
|
|
4769
|
+
if (destroyRequested)
|
|
4770
|
+
return;
|
|
4771
|
+
destroyRequested = true;
|
|
3868
4772
|
const root = roots.get(canvas);
|
|
3869
|
-
if (root
|
|
4773
|
+
if (root &&
|
|
4774
|
+
root === store &&
|
|
4775
|
+
rootLifecycles.get(canvas) === lifecycle &&
|
|
4776
|
+
lifecycle.owner === owner) {
|
|
3870
4777
|
root.update((state) => ({ internal: { ...state.internal, active: false } }));
|
|
3871
|
-
|
|
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;
|
|
3872
4786
|
try {
|
|
3873
4787
|
const state = root.snapshot;
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
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
|
+
}
|
|
3882
4807
|
}
|
|
3883
|
-
|
|
3884
|
-
|
|
4808
|
+
finally {
|
|
4809
|
+
if (roots.get(canvas) === root)
|
|
4810
|
+
roots.delete(canvas);
|
|
4811
|
+
if (rootLifecycles.get(canvas) === lifecycle)
|
|
4812
|
+
rootLifecycles.delete(canvas);
|
|
3885
4813
|
}
|
|
3886
4814
|
}, timeout);
|
|
4815
|
+
lifecycle.teardown = teardown;
|
|
3887
4816
|
}
|
|
3888
4817
|
},
|
|
3889
4818
|
configure: (inputs) => {
|
|
4819
|
+
if (destroyRequested || rootLifecycles.get(canvas) !== lifecycle || lifecycle.owner !== owner)
|
|
4820
|
+
return;
|
|
3890
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;
|
|
3891
4822
|
const state = store.snapshot;
|
|
3892
4823
|
const stateToUpdate = {};
|
|
@@ -3905,44 +4836,20 @@ function canvasRootInitializer(injector) {
|
|
|
3905
4836
|
if (!is.equ(params, raycaster.params, shallowLoose)) {
|
|
3906
4837
|
applyProps(raycaster, { params: { ...raycaster.params, ...(params || {}) } });
|
|
3907
4838
|
}
|
|
3908
|
-
|
|
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.
|
|
3909
4846
|
if (!state.camera ||
|
|
3910
|
-
(state.camera ===
|
|
3911
|
-
|
|
3912
|
-
const
|
|
3913
|
-
let camera = isCamera
|
|
3914
|
-
? cameraOptions
|
|
3915
|
-
: makeCameraInstance(orthographic, sizeOptions ?? state.size);
|
|
3916
|
-
if (!isCamera) {
|
|
3917
|
-
camera.position.z = 5;
|
|
3918
|
-
if (cameraOptions) {
|
|
3919
|
-
applyProps(camera, cameraOptions);
|
|
3920
|
-
if ('aspect' in cameraOptions ||
|
|
3921
|
-
'left' in cameraOptions ||
|
|
3922
|
-
'right' in cameraOptions ||
|
|
3923
|
-
'top' in cameraOptions ||
|
|
3924
|
-
'bottom' in cameraOptions) {
|
|
3925
|
-
Object.assign(camera, { manual: true });
|
|
3926
|
-
camera?.updateProjectionMatrix();
|
|
3927
|
-
}
|
|
3928
|
-
}
|
|
3929
|
-
// always look at center or passed-in lookAt by default
|
|
3930
|
-
if (!state.camera && !cameraOptions?.rotation && !cameraOptions?.quaternion) {
|
|
3931
|
-
if (Array.isArray(lookAt))
|
|
3932
|
-
camera.lookAt(lookAt[0], lookAt[1], lookAt[2]);
|
|
3933
|
-
else if (typeof lookAt === 'number')
|
|
3934
|
-
camera.lookAt(lookAt, lookAt, lookAt);
|
|
3935
|
-
else if (lookAt?.isVector3)
|
|
3936
|
-
camera.lookAt(lookAt);
|
|
3937
|
-
else
|
|
3938
|
-
camera.lookAt(0, 0, 0);
|
|
3939
|
-
}
|
|
3940
|
-
// update projection matrix after applyprops
|
|
3941
|
-
camera.updateProjectionMatrix?.();
|
|
3942
|
-
}
|
|
3943
|
-
if (!is.instance(camera))
|
|
3944
|
-
camera = prepare(camera, '', { store });
|
|
4847
|
+
(state.camera === lifecycle.managedCamera &&
|
|
4848
|
+
!cameraConfigurationsEqual(lifecycle.appliedCameraConfiguration, cameraConfiguration))) {
|
|
4849
|
+
const camera = createManagedCamera(cameraConfiguration, store, sizeOptions ?? state.size);
|
|
3945
4850
|
stateToUpdate.camera = camera;
|
|
4851
|
+
lifecycle.managedCamera = camera;
|
|
4852
|
+
lifecycle.appliedCameraConfiguration = cameraConfiguration;
|
|
3946
4853
|
// Configure raycaster
|
|
3947
4854
|
// https://github.com/pmndrs/react-xr/issues/300
|
|
3948
4855
|
raycaster.camera = camera;
|
|
@@ -4311,15 +5218,24 @@ function elementEvents(target, events, { injector } = {}) {
|
|
|
4311
5218
|
const targetObj = resolveRef(target());
|
|
4312
5219
|
if (!targetObj || !is.instance(targetObj))
|
|
4313
5220
|
return;
|
|
5221
|
+
const currentCleanUps = [];
|
|
4314
5222
|
Object.keys(events).forEach((eventName) => {
|
|
4315
|
-
|
|
5223
|
+
const cleanUp = renderer.listen(targetObj, eventName, events[eventName]);
|
|
5224
|
+
currentCleanUps.push(cleanUp);
|
|
5225
|
+
cleanUps.push(cleanUp);
|
|
4316
5226
|
});
|
|
4317
5227
|
onCleanup(() => {
|
|
4318
|
-
|
|
5228
|
+
for (const cleanUp of currentCleanUps) {
|
|
5229
|
+
cleanUp();
|
|
5230
|
+
const index = cleanUps.indexOf(cleanUp);
|
|
5231
|
+
if (index >= 0)
|
|
5232
|
+
cleanUps.splice(index, 1);
|
|
5233
|
+
}
|
|
4319
5234
|
});
|
|
4320
5235
|
});
|
|
4321
5236
|
inject(DestroyRef).onDestroy(() => {
|
|
4322
|
-
|
|
5237
|
+
for (const cleanUp of cleanUps.splice(0))
|
|
5238
|
+
cleanUp();
|
|
4323
5239
|
});
|
|
4324
5240
|
return cleanUps;
|
|
4325
5241
|
});
|
|
@@ -4438,15 +5354,24 @@ function objectEvents(target, events, { injector } = {}) {
|
|
|
4438
5354
|
const targetObj = resolveRef(target());
|
|
4439
5355
|
if (!targetObj || !is.instance(targetObj))
|
|
4440
5356
|
return;
|
|
5357
|
+
const currentCleanUps = [];
|
|
4441
5358
|
Object.entries(events).forEach(([eventName, eventHandler]) => {
|
|
4442
|
-
|
|
5359
|
+
const cleanUp = renderer.listen(targetObj, eventName, eventHandler);
|
|
5360
|
+
currentCleanUps.push(cleanUp);
|
|
5361
|
+
cleanUps.push(cleanUp);
|
|
4443
5362
|
});
|
|
4444
5363
|
onCleanup(() => {
|
|
4445
|
-
|
|
5364
|
+
for (const cleanUp of currentCleanUps) {
|
|
5365
|
+
cleanUp();
|
|
5366
|
+
const index = cleanUps.indexOf(cleanUp);
|
|
5367
|
+
if (index >= 0)
|
|
5368
|
+
cleanUps.splice(index, 1);
|
|
5369
|
+
}
|
|
4446
5370
|
});
|
|
4447
5371
|
});
|
|
4448
5372
|
inject(DestroyRef).onDestroy(() => {
|
|
4449
|
-
|
|
5373
|
+
for (const cleanUp of cleanUps.splice(0))
|
|
5374
|
+
cleanUp();
|
|
4450
5375
|
});
|
|
4451
5376
|
return cleanUps;
|
|
4452
5377
|
});
|
|
@@ -4478,5 +5403,5 @@ function hasListener(...emitterRefs) {
|
|
|
4478
5403
|
* Generated bundle index. Do not edit.
|
|
4479
5404
|
*/
|
|
4480
5405
|
|
|
4481
|
-
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 };
|
|
4482
5407
|
//# sourceMappingURL=angular-three.mjs.map
|