angular-three 4.0.0-next.104 → 4.0.0-next.105
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.
|
@@ -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, ElementRef, booleanAttribute, resource, Pipe, numberAttribute, contentChild, SkipSelf, ChangeDetectionStrategy, CUSTOM_ELEMENTS_SCHEMA, Component,
|
|
2
|
+
import { inject, ViewContainerRef, TemplateRef, Injector, effect, DestroyRef, Directive, input, linkedSignal, computed, InjectionToken, untracked, isSignal, signal, Injectable, ElementRef, booleanAttribute, resource, Pipe, numberAttribute, contentChild, SkipSelf, ChangeDetectionStrategy, CUSTOM_ELEMENTS_SCHEMA, Component, output, model, Renderer2 } from '@angular/core';
|
|
3
3
|
import { DOCUMENT } from '@angular/common';
|
|
4
4
|
import { Subject, filter } from 'rxjs';
|
|
5
5
|
import * as THREE from 'three';
|
|
@@ -295,59 +295,6 @@ function injectLoop() {
|
|
|
295
295
|
return inject(NGT_LOOP);
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
-
const idCache = {};
|
|
299
|
-
function makeId(event) {
|
|
300
|
-
if (event) {
|
|
301
|
-
return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
|
|
302
|
-
}
|
|
303
|
-
const newId = THREE.MathUtils.generateUUID();
|
|
304
|
-
// ensure not already used
|
|
305
|
-
if (!idCache[newId]) {
|
|
306
|
-
idCache[newId] = true;
|
|
307
|
-
return newId;
|
|
308
|
-
}
|
|
309
|
-
return makeId();
|
|
310
|
-
}
|
|
311
|
-
function makeDpr(dpr, window) {
|
|
312
|
-
// Err on the side of progress by assuming 2x dpr if we can't detect it
|
|
313
|
-
// This will happen in workers where window is defined but dpr isn't.
|
|
314
|
-
const target = typeof window !== 'undefined' ? (window.devicePixelRatio ?? 2) : 1;
|
|
315
|
-
return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], target), dpr[1]) : dpr;
|
|
316
|
-
}
|
|
317
|
-
function makeRendererInstance(glOptions, canvas) {
|
|
318
|
-
const defaultOptions = {
|
|
319
|
-
powerPreference: 'high-performance',
|
|
320
|
-
canvas,
|
|
321
|
-
antialias: true,
|
|
322
|
-
alpha: true,
|
|
323
|
-
};
|
|
324
|
-
const customRenderer = (typeof glOptions === 'function' ? glOptions(defaultOptions) : glOptions);
|
|
325
|
-
if (is.renderer(customRenderer))
|
|
326
|
-
return customRenderer;
|
|
327
|
-
return new THREE.WebGLRenderer({ ...defaultOptions, ...glOptions });
|
|
328
|
-
}
|
|
329
|
-
function makeCameraInstance(isOrthographic, size) {
|
|
330
|
-
if (isOrthographic)
|
|
331
|
-
return new THREE.OrthographicCamera(0, 0, 0, 0, 0.1, 1000);
|
|
332
|
-
return new THREE.PerspectiveCamera(75, size.width / size.height, 0.1, 1000);
|
|
333
|
-
}
|
|
334
|
-
function makeObjectGraph(object) {
|
|
335
|
-
const data = { nodes: {}, materials: {}, meshes: {} };
|
|
336
|
-
if (object) {
|
|
337
|
-
object.traverse((child) => {
|
|
338
|
-
if (child.name)
|
|
339
|
-
data.nodes[child.name] = child;
|
|
340
|
-
if ('material' in child && !data.materials[child.material.name]) {
|
|
341
|
-
data.materials[child.material.name] = child
|
|
342
|
-
.material;
|
|
343
|
-
}
|
|
344
|
-
if (is.three(child, 'isMesh') && !data.meshes[child.name])
|
|
345
|
-
data.meshes[child.name] = child;
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
return data;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
298
|
/** ported from ngrx/signals */
|
|
352
299
|
const STATE_SOURCE = Symbol('STATE_SOURCE');
|
|
353
300
|
function patchState(stateSource, ...updaters) {
|
|
@@ -444,349 +391,137 @@ function updateCamera(camera, size) {
|
|
|
444
391
|
}
|
|
445
392
|
}
|
|
446
393
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
394
|
+
/**
|
|
395
|
+
* @deprecated: use `getInstanceState` instead. Will be removed in 5.0.0
|
|
396
|
+
*/
|
|
397
|
+
function getLocalState(obj) {
|
|
398
|
+
return getInstanceState(obj);
|
|
399
|
+
}
|
|
400
|
+
function getInstanceState(obj) {
|
|
401
|
+
if (!obj)
|
|
402
|
+
return undefined;
|
|
403
|
+
return obj.__ngt__ || undefined;
|
|
404
|
+
}
|
|
405
|
+
function invalidateInstance(instance) {
|
|
406
|
+
let store = getInstanceState(instance)?.store;
|
|
407
|
+
if (store) {
|
|
408
|
+
while (store.snapshot.previousRoot) {
|
|
409
|
+
store = store.snapshot.previousRoot;
|
|
410
|
+
}
|
|
411
|
+
if (store.snapshot.internal.frames === 0) {
|
|
412
|
+
store.snapshot.invalidate();
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
checkUpdate(instance);
|
|
416
|
+
}
|
|
417
|
+
function prepare(object, type, instanceState) {
|
|
418
|
+
const instance = object;
|
|
419
|
+
if (instanceState?.type === 'ngt-primitive' || !instance.__ngt__) {
|
|
420
|
+
const { hierarchyStore = signalState({
|
|
421
|
+
parent: null,
|
|
422
|
+
objects: [],
|
|
423
|
+
nonObjects: [],
|
|
424
|
+
geometryStamp: Date.now(),
|
|
425
|
+
}), store = null, ...rest } = instanceState || {};
|
|
426
|
+
const nonObjects = hierarchyStore.nonObjects;
|
|
427
|
+
const geometryStamp = hierarchyStore.geometryStamp;
|
|
428
|
+
const nonObjectsChanged = computed(() => {
|
|
429
|
+
const [_nonObjects] = [nonObjects(), geometryStamp()];
|
|
430
|
+
return _nonObjects;
|
|
431
|
+
});
|
|
432
|
+
instance.__ngt_id__ = crypto.randomUUID();
|
|
433
|
+
instance.__ngt__ = {
|
|
434
|
+
previousAttach: null,
|
|
435
|
+
type,
|
|
436
|
+
eventCount: 0,
|
|
437
|
+
handlers: {},
|
|
438
|
+
hierarchyStore,
|
|
439
|
+
object: instance,
|
|
440
|
+
parent: hierarchyStore.parent,
|
|
441
|
+
objects: hierarchyStore.objects,
|
|
442
|
+
nonObjects: nonObjectsChanged,
|
|
443
|
+
add(object, type) {
|
|
444
|
+
const current = instance.__ngt__.hierarchyStore.snapshot[type];
|
|
445
|
+
const foundIndex = current.findIndex((node) => object === node || (!!object['uuid'] && !!node['uuid'] && object['uuid'] === node['uuid']));
|
|
446
|
+
if (foundIndex > -1) {
|
|
447
|
+
current.splice(foundIndex, 1, object);
|
|
448
|
+
instance.__ngt__.hierarchyStore.update({ [type]: current });
|
|
449
|
+
}
|
|
450
|
+
else {
|
|
451
|
+
instance.__ngt__.hierarchyStore.update((prev) => ({ [type]: [...prev[type], object] }));
|
|
452
|
+
}
|
|
453
|
+
notifyAncestors(instance.__ngt__.hierarchyStore.snapshot.parent, type);
|
|
454
|
+
},
|
|
455
|
+
remove(object, type) {
|
|
456
|
+
instance.__ngt__.hierarchyStore.update((prev) => ({
|
|
457
|
+
[type]: prev[type].filter((node) => node !== object),
|
|
458
|
+
}));
|
|
459
|
+
notifyAncestors(instance.__ngt__.hierarchyStore.snapshot.parent, type);
|
|
460
|
+
},
|
|
461
|
+
setParent(parent) {
|
|
462
|
+
instance.__ngt__.hierarchyStore.update({ parent });
|
|
463
|
+
},
|
|
464
|
+
updateGeometryStamp() {
|
|
465
|
+
instance.__ngt__.hierarchyStore.update({ geometryStamp: Date.now() });
|
|
496
466
|
},
|
|
467
|
+
store,
|
|
468
|
+
...rest,
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
Object.defineProperty(instance.__ngt__, 'setPointerEvent', {
|
|
472
|
+
value: (eventName, callback) => {
|
|
473
|
+
const iS = getInstanceState(instance);
|
|
474
|
+
if (!iS.handlers)
|
|
475
|
+
iS.handlers = {};
|
|
476
|
+
// try to get the previous handler. compound might have one, the THREE object might also have one with the same name
|
|
477
|
+
const previousHandler = iS.handlers[eventName];
|
|
478
|
+
// readjust the callback
|
|
479
|
+
const updatedCallback = (event) => {
|
|
480
|
+
if (previousHandler)
|
|
481
|
+
previousHandler(event);
|
|
482
|
+
callback(event);
|
|
483
|
+
};
|
|
484
|
+
Object.assign(iS.handlers, { [eventName]: updatedCallback });
|
|
485
|
+
// increment the count everytime
|
|
486
|
+
iS.eventCount += 1;
|
|
487
|
+
// clean up the event listener by removing the target from the interaction array
|
|
488
|
+
return () => {
|
|
489
|
+
const iS = getInstanceState(instance);
|
|
490
|
+
if (iS) {
|
|
491
|
+
iS.handlers && delete iS.handlers[eventName];
|
|
492
|
+
iS.eventCount -= 1;
|
|
493
|
+
}
|
|
494
|
+
};
|
|
497
495
|
},
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
if (
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
width: width / camera.zoom,
|
|
520
|
-
height: height / camera.zoom,
|
|
521
|
-
top,
|
|
522
|
-
left,
|
|
523
|
-
factor: 1,
|
|
524
|
-
distance,
|
|
525
|
-
aspect,
|
|
526
|
-
};
|
|
496
|
+
configurable: true,
|
|
497
|
+
});
|
|
498
|
+
Object.defineProperties(instance.__ngt__, {
|
|
499
|
+
addInteraction: {
|
|
500
|
+
value: (store) => {
|
|
501
|
+
if (!store)
|
|
502
|
+
return;
|
|
503
|
+
const iS = getInstanceState(instance);
|
|
504
|
+
if (iS.eventCount < 1 || !('raycast' in instance) || !instance['raycast'])
|
|
505
|
+
return;
|
|
506
|
+
let root = store;
|
|
507
|
+
while (root.snapshot.previousRoot) {
|
|
508
|
+
root = root.snapshot.previousRoot;
|
|
509
|
+
}
|
|
510
|
+
if (root.snapshot.internal) {
|
|
511
|
+
const interactions = root.snapshot.internal.interaction;
|
|
512
|
+
const index = interactions.findIndex((obj) => obj.uuid === instance.uuid);
|
|
513
|
+
// if already exists, do not add to interactions
|
|
514
|
+
if (index < 0) {
|
|
515
|
+
root.snapshot.internal.interaction.push(instance);
|
|
516
|
+
}
|
|
527
517
|
}
|
|
528
|
-
const fov = (camera.fov * Math.PI) / 180; // convert vertical fov to radians
|
|
529
|
-
const h = 2 * Math.tan(fov / 2) * distance; // visible height
|
|
530
|
-
const w = h * (width / height);
|
|
531
|
-
return { width: w, height: h, top, left, factor: width / w, distance, aspect };
|
|
532
518
|
},
|
|
519
|
+
configurable: true,
|
|
533
520
|
},
|
|
534
|
-
|
|
535
|
-
setSize: (width, height, top, left) => {
|
|
536
|
-
const camera = store.snapshot.camera;
|
|
537
|
-
const size = { width, height, top: top ?? 0, left: left ?? 0 };
|
|
538
|
-
store.update((state) => ({
|
|
539
|
-
size,
|
|
540
|
-
viewport: {
|
|
541
|
-
...state.viewport,
|
|
542
|
-
...state.viewport.getCurrentViewport(camera, defaultTarget, size),
|
|
543
|
-
},
|
|
544
|
-
}));
|
|
545
|
-
},
|
|
546
|
-
setDpr: (dpr) => {
|
|
547
|
-
const resolved = makeDpr(dpr, window);
|
|
548
|
-
store.update((state) => ({
|
|
549
|
-
viewport: { ...state.viewport, dpr: resolved, initialDpr: state.viewport.initialDpr || resolved },
|
|
550
|
-
}));
|
|
551
|
-
},
|
|
552
|
-
setFrameloop: (frameloop) => {
|
|
553
|
-
const clock = store.snapshot.clock;
|
|
554
|
-
// if frameloop === "never" clock.elapsedTime is updated using advance(timestamp)
|
|
555
|
-
clock.stop();
|
|
556
|
-
clock.elapsedTime = 0;
|
|
557
|
-
if (frameloop !== 'never') {
|
|
558
|
-
clock.start();
|
|
559
|
-
clock.elapsedTime = 0;
|
|
560
|
-
}
|
|
561
|
-
store.update(() => ({ frameloop }));
|
|
562
|
-
},
|
|
563
|
-
previousRoot: null,
|
|
564
|
-
internal: {
|
|
565
|
-
active: false,
|
|
566
|
-
priority: 0,
|
|
567
|
-
frames: 0,
|
|
568
|
-
lastEvent: new ElementRef(null),
|
|
569
|
-
interaction: [],
|
|
570
|
-
hovered: new Map(),
|
|
571
|
-
capturedMap: new Map(),
|
|
572
|
-
initialClick: [0, 0],
|
|
573
|
-
initialHits: [],
|
|
574
|
-
subscribers: [],
|
|
575
|
-
subscribe: (callback, priority = 0, _store = store) => {
|
|
576
|
-
const internal = _store.snapshot.internal;
|
|
577
|
-
// If this subscription was given a priority, it takes rendering into its own hands
|
|
578
|
-
// For that reason we switch off automatic rendering and increase the manual flag
|
|
579
|
-
// As long as this flag is positive there can be no internal rendering at all
|
|
580
|
-
// because there could be multiple render subscriptions
|
|
581
|
-
internal.priority = internal.priority + (priority > 0 ? 1 : 0);
|
|
582
|
-
internal.subscribers.push({ callback, priority, store: _store });
|
|
583
|
-
// Register subscriber and sort layers from lowest to highest, meaning,
|
|
584
|
-
// highest priority renders last (on top of the other frames)
|
|
585
|
-
internal.subscribers = internal.subscribers.sort((a, b) => (a.priority || 0) - (b.priority || 0));
|
|
586
|
-
return () => {
|
|
587
|
-
const internal = _store.snapshot.internal;
|
|
588
|
-
if (internal?.subscribers) {
|
|
589
|
-
// Decrease manual flag if this subscription had a priority
|
|
590
|
-
internal.priority = internal.priority - (priority > 0 ? 1 : 0);
|
|
591
|
-
// Remove subscriber from list
|
|
592
|
-
internal.subscribers = internal.subscribers.filter((s) => s.callback !== callback);
|
|
593
|
-
}
|
|
594
|
-
};
|
|
595
|
-
},
|
|
596
|
-
},
|
|
597
|
-
});
|
|
598
|
-
Object.defineProperty(store, '__pointerMissed$', { get: () => pointerMissed$ });
|
|
599
|
-
let { size: oldSize, viewport: { dpr: oldDpr }, camera: oldCamera, } = store.snapshot;
|
|
600
|
-
effect(() => {
|
|
601
|
-
const [newCamera, newSize, newDpr, gl] = [store.camera(), store.size(), store.viewport.dpr(), store.gl()];
|
|
602
|
-
// Resize camera and renderer on changes to size and pixel-ratio
|
|
603
|
-
if (newSize !== oldSize || newDpr !== oldDpr) {
|
|
604
|
-
oldSize = newSize;
|
|
605
|
-
oldDpr = newDpr;
|
|
606
|
-
// Update camera & renderer
|
|
607
|
-
updateCamera(newCamera, newSize);
|
|
608
|
-
gl.setPixelRatio(newDpr);
|
|
609
|
-
const updateStyle = typeof HTMLCanvasElement !== 'undefined' && gl.domElement instanceof HTMLCanvasElement;
|
|
610
|
-
gl.setSize(newSize.width, newSize.height, updateStyle);
|
|
611
|
-
}
|
|
612
|
-
// Update viewport once the camera changes
|
|
613
|
-
if (newCamera !== oldCamera) {
|
|
614
|
-
oldCamera = newCamera;
|
|
615
|
-
updateCamera(newCamera, newSize);
|
|
616
|
-
// Update viewport
|
|
617
|
-
store.update((state) => ({
|
|
618
|
-
viewport: { ...state.viewport, ...state.viewport.getCurrentViewport(newCamera) },
|
|
619
|
-
}));
|
|
620
|
-
}
|
|
621
|
-
});
|
|
622
|
-
return store;
|
|
623
|
-
}
|
|
624
|
-
const NGT_STORE = new InjectionToken('NgtStore Token');
|
|
625
|
-
function injectStore(options) {
|
|
626
|
-
return inject(NGT_STORE, options);
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
function resolveRef(ref) {
|
|
630
|
-
if (is.ref(ref)) {
|
|
631
|
-
return ref.nativeElement;
|
|
632
|
-
}
|
|
633
|
-
return ref;
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
class NgtParent extends NgtCommonDirective {
|
|
637
|
-
constructor() {
|
|
638
|
-
super();
|
|
639
|
-
this.parent = input.required();
|
|
640
|
-
this.store = injectStore();
|
|
641
|
-
this._parent = computed(() => {
|
|
642
|
-
const parent = this.parent();
|
|
643
|
-
const rawParent = typeof parent === 'function' ? parent() : parent;
|
|
644
|
-
if (!rawParent)
|
|
645
|
-
return null;
|
|
646
|
-
const scene = this.store.scene();
|
|
647
|
-
if (typeof rawParent === 'string') {
|
|
648
|
-
return scene.getObjectByName(rawParent);
|
|
649
|
-
}
|
|
650
|
-
return resolveRef(rawParent);
|
|
651
|
-
});
|
|
652
|
-
this.linkedValue = linkedSignal(this._parent);
|
|
653
|
-
this.shouldSkipRender = computed(() => !this._parent());
|
|
654
|
-
const commentNode = this.commentNode;
|
|
655
|
-
commentNode.data = NGT_PARENT_FLAG;
|
|
656
|
-
commentNode[NGT_PARENT_FLAG] = true;
|
|
657
|
-
if (commentNode[NGT_INTERNAL_ADD_COMMENT_FLAG]) {
|
|
658
|
-
commentNode[NGT_INTERNAL_ADD_COMMENT_FLAG]('parent', this.injector);
|
|
659
|
-
delete commentNode[NGT_INTERNAL_ADD_COMMENT_FLAG];
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
validate() {
|
|
663
|
-
return !this.injected && !!this.injectedValue;
|
|
664
|
-
}
|
|
665
|
-
beforeCreateView() {
|
|
666
|
-
const commentNode = this.commentNode;
|
|
667
|
-
if (commentNode[NGT_INTERNAL_SET_PARENT_COMMENT_FLAG]) {
|
|
668
|
-
commentNode[NGT_INTERNAL_SET_PARENT_COMMENT_FLAG](this.injectedValue);
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgtParent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
672
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.11", type: NgtParent, isStandalone: true, selector: "ng-template[parent]", inputs: { parent: { classPropertyName: "parent", publicName: "parent", isSignal: true, isRequired: true, transformFunction: null } }, usesInheritance: true, ngImport: i0 }); }
|
|
673
|
-
}
|
|
674
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgtParent, decorators: [{
|
|
675
|
-
type: Directive,
|
|
676
|
-
args: [{ selector: 'ng-template[parent]' }]
|
|
677
|
-
}], ctorParameters: () => [] });
|
|
678
|
-
|
|
679
|
-
/**
|
|
680
|
-
* @deprecated: use `getInstanceState` instead. Will be removed in 5.0.0
|
|
681
|
-
*/
|
|
682
|
-
function getLocalState(obj) {
|
|
683
|
-
return getInstanceState(obj);
|
|
684
|
-
}
|
|
685
|
-
function getInstanceState(obj) {
|
|
686
|
-
if (!obj)
|
|
687
|
-
return undefined;
|
|
688
|
-
return obj.__ngt__ || undefined;
|
|
689
|
-
}
|
|
690
|
-
function invalidateInstance(instance) {
|
|
691
|
-
let store = getInstanceState(instance)?.store;
|
|
692
|
-
if (store) {
|
|
693
|
-
while (store.snapshot.previousRoot) {
|
|
694
|
-
store = store.snapshot.previousRoot;
|
|
695
|
-
}
|
|
696
|
-
if (store.snapshot.internal.frames === 0) {
|
|
697
|
-
store.snapshot.invalidate();
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
checkUpdate(instance);
|
|
701
|
-
}
|
|
702
|
-
function prepare(object, type, instanceState) {
|
|
703
|
-
const instance = object;
|
|
704
|
-
if (instanceState?.type === 'ngt-primitive' || !instance.__ngt__) {
|
|
705
|
-
const { hierarchyStore = signalState({
|
|
706
|
-
parent: null,
|
|
707
|
-
objects: [],
|
|
708
|
-
nonObjects: [],
|
|
709
|
-
geometryStamp: Date.now(),
|
|
710
|
-
}), store = null, ...rest } = instanceState || {};
|
|
711
|
-
const nonObjects = hierarchyStore.nonObjects;
|
|
712
|
-
const geometryStamp = hierarchyStore.geometryStamp;
|
|
713
|
-
const nonObjectsChanged = computed(() => {
|
|
714
|
-
const [_nonObjects] = [nonObjects(), geometryStamp()];
|
|
715
|
-
return _nonObjects;
|
|
716
|
-
});
|
|
717
|
-
instance.__ngt__ = {
|
|
718
|
-
previousAttach: null,
|
|
719
|
-
type,
|
|
720
|
-
eventCount: 0,
|
|
721
|
-
handlers: {},
|
|
722
|
-
hierarchyStore,
|
|
723
|
-
object: instance,
|
|
724
|
-
parent: hierarchyStore.parent,
|
|
725
|
-
objects: hierarchyStore.objects,
|
|
726
|
-
nonObjects: nonObjectsChanged,
|
|
727
|
-
add(object, type) {
|
|
728
|
-
const current = instance.__ngt__.hierarchyStore.snapshot[type];
|
|
729
|
-
const foundIndex = current.findIndex((node) => object === node || (!!object['uuid'] && !!node['uuid'] && object['uuid'] === node['uuid']));
|
|
730
|
-
if (foundIndex > -1) {
|
|
731
|
-
current.splice(foundIndex, 1, object);
|
|
732
|
-
instance.__ngt__.hierarchyStore.update({ [type]: current });
|
|
733
|
-
}
|
|
734
|
-
else {
|
|
735
|
-
instance.__ngt__.hierarchyStore.update((prev) => ({ [type]: [...prev[type], object] }));
|
|
736
|
-
}
|
|
737
|
-
notifyAncestors(instance.__ngt__.hierarchyStore.snapshot.parent, type);
|
|
738
|
-
},
|
|
739
|
-
remove(object, type) {
|
|
740
|
-
instance.__ngt__.hierarchyStore.update((prev) => ({
|
|
741
|
-
[type]: prev[type].filter((node) => node !== object),
|
|
742
|
-
}));
|
|
743
|
-
notifyAncestors(instance.__ngt__.hierarchyStore.snapshot.parent, type);
|
|
744
|
-
},
|
|
745
|
-
setParent(parent) {
|
|
746
|
-
instance.__ngt__.hierarchyStore.update({ parent });
|
|
747
|
-
},
|
|
748
|
-
updateGeometryStamp() {
|
|
749
|
-
instance.__ngt__.hierarchyStore.update({ geometryStamp: Date.now() });
|
|
750
|
-
},
|
|
751
|
-
store,
|
|
752
|
-
...rest,
|
|
753
|
-
};
|
|
754
|
-
}
|
|
755
|
-
Object.defineProperty(instance.__ngt__, 'setPointerEvent', {
|
|
756
|
-
value: (eventName, callback) => {
|
|
757
|
-
const iS = getInstanceState(instance);
|
|
758
|
-
if (!iS.handlers)
|
|
759
|
-
iS.handlers = {};
|
|
760
|
-
// try to get the previous handler. compound might have one, the THREE object might also have one with the same name
|
|
761
|
-
const previousHandler = iS.handlers[eventName];
|
|
762
|
-
// readjust the callback
|
|
763
|
-
const updatedCallback = (event) => {
|
|
764
|
-
if (previousHandler)
|
|
765
|
-
previousHandler(event);
|
|
766
|
-
callback(event);
|
|
767
|
-
};
|
|
768
|
-
Object.assign(iS.handlers, { [eventName]: updatedCallback });
|
|
769
|
-
// increment the count everytime
|
|
770
|
-
iS.eventCount += 1;
|
|
771
|
-
// clean up the event listener by removing the target from the interaction array
|
|
772
|
-
return () => {
|
|
773
|
-
const iS = getInstanceState(instance);
|
|
774
|
-
if (iS) {
|
|
775
|
-
iS.handlers && delete iS.handlers[eventName];
|
|
776
|
-
iS.eventCount -= 1;
|
|
777
|
-
}
|
|
778
|
-
};
|
|
779
|
-
},
|
|
780
|
-
configurable: true,
|
|
781
|
-
});
|
|
782
|
-
Object.defineProperties(instance.__ngt__, {
|
|
783
|
-
addInteraction: {
|
|
521
|
+
removeInteraction: {
|
|
784
522
|
value: (store) => {
|
|
785
523
|
if (!store)
|
|
786
524
|
return;
|
|
787
|
-
const iS = getInstanceState(instance);
|
|
788
|
-
if (iS.eventCount < 1 || !('raycast' in instance) || !instance['raycast'])
|
|
789
|
-
return;
|
|
790
525
|
let root = store;
|
|
791
526
|
while (root.snapshot.previousRoot) {
|
|
792
527
|
root = root.snapshot.previousRoot;
|
|
@@ -794,27 +529,8 @@ function prepare(object, type, instanceState) {
|
|
|
794
529
|
if (root.snapshot.internal) {
|
|
795
530
|
const interactions = root.snapshot.internal.interaction;
|
|
796
531
|
const index = interactions.findIndex((obj) => obj.uuid === instance.uuid);
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
root.snapshot.internal.interaction.push(instance);
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
},
|
|
803
|
-
configurable: true,
|
|
804
|
-
},
|
|
805
|
-
removeInteraction: {
|
|
806
|
-
value: (store) => {
|
|
807
|
-
if (!store)
|
|
808
|
-
return;
|
|
809
|
-
let root = store;
|
|
810
|
-
while (root.snapshot.previousRoot) {
|
|
811
|
-
root = root.snapshot.previousRoot;
|
|
812
|
-
}
|
|
813
|
-
if (root.snapshot.internal) {
|
|
814
|
-
const interactions = root.snapshot.internal.interaction;
|
|
815
|
-
const index = interactions.findIndex((obj) => obj.uuid === instance.uuid);
|
|
816
|
-
if (index >= 0)
|
|
817
|
-
interactions.splice(index, 1);
|
|
532
|
+
if (index >= 0)
|
|
533
|
+
interactions.splice(index, 1);
|
|
818
534
|
}
|
|
819
535
|
},
|
|
820
536
|
configurable: true,
|
|
@@ -822,175 +538,398 @@ function prepare(object, type, instanceState) {
|
|
|
822
538
|
});
|
|
823
539
|
return instance;
|
|
824
540
|
}
|
|
541
|
+
const notificationCache = new Map();
|
|
542
|
+
/**
|
|
543
|
+
* Notify ancestors about changes to a THREE.js objects' children
|
|
544
|
+
*
|
|
545
|
+
* For example: `NgtsCenter` might have a child that asynchronously loads a 3D model
|
|
546
|
+
* in which case the model matrices will be settled later. `NgtsCenter` needs to know about this
|
|
547
|
+
* matrices change to re-center everything inside of it.
|
|
548
|
+
*
|
|
549
|
+
* The implementation here uses a naive approach to reduce the number of notifications; we cache
|
|
550
|
+
* the notifications by the instance ID and the type of the notification.
|
|
551
|
+
*
|
|
552
|
+
* 1. If there's no cache or
|
|
553
|
+
* 2. If the type is different for the same instance or
|
|
554
|
+
* 3. We've skipped the notifications for this instance more than a certain amount
|
|
555
|
+
*
|
|
556
|
+
* then we'll proceed with notification
|
|
557
|
+
*/
|
|
825
558
|
function notifyAncestors(instance, type) {
|
|
826
559
|
if (!instance)
|
|
827
560
|
return;
|
|
828
561
|
const localState = getInstanceState(instance);
|
|
829
562
|
if (!localState)
|
|
830
563
|
return;
|
|
831
|
-
const
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
this.source.update(...args);
|
|
564
|
+
const id = instance.__ngt_id__ || instance['uuid'];
|
|
565
|
+
if (!id)
|
|
566
|
+
return;
|
|
567
|
+
const maxNotificationSkipCount = localState.store?.snapshot.maxNotificationSkipCount || 5;
|
|
568
|
+
const cached = notificationCache.get(id);
|
|
569
|
+
if (!cached || cached.lastType !== type || cached.skipCount > maxNotificationSkipCount) {
|
|
570
|
+
notificationCache.set(id, { skipCount: 0, lastType: type });
|
|
571
|
+
if (notificationCache.size === 1) {
|
|
572
|
+
queueMicrotask(() => notificationCache.clear());
|
|
573
|
+
}
|
|
574
|
+
const { parent } = localState.hierarchyStore.snapshot;
|
|
575
|
+
localState.hierarchyStore.update({ [type]: (localState.hierarchyStore.snapshot[type] || []).slice() });
|
|
576
|
+
notifyAncestors(parent, type);
|
|
577
|
+
return;
|
|
846
578
|
}
|
|
847
|
-
|
|
848
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.11", type: NgtSelectionApi, isStandalone: true, selector: "[selection]", inputs: { enabled: { classPropertyName: "enabled", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
|
|
579
|
+
notificationCache.set(id, { ...cached, skipCount: cached.skipCount + 1 });
|
|
849
580
|
}
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
const selectionEnabled = selectionApi.enabled();
|
|
861
|
-
if (!selectionEnabled)
|
|
862
|
-
return;
|
|
863
|
-
const enabled = this.enabled();
|
|
864
|
-
if (!enabled)
|
|
865
|
-
return;
|
|
866
|
-
const host = elementRef.nativeElement;
|
|
867
|
-
const localState = getInstanceState(host);
|
|
868
|
-
if (!localState)
|
|
869
|
-
return;
|
|
870
|
-
// ngt-mesh[select]
|
|
871
|
-
if (host.type === 'Mesh') {
|
|
872
|
-
selectionApi.update((prev) => [...prev, host]);
|
|
873
|
-
onCleanup(() => selectionApi.update((prev) => prev.filter((el) => el !== host)));
|
|
874
|
-
return;
|
|
581
|
+
|
|
582
|
+
// This function prepares a set of changes to be applied to the instance
|
|
583
|
+
function diffProps(instance, props) {
|
|
584
|
+
const changes = [];
|
|
585
|
+
for (const propKey in props) {
|
|
586
|
+
const propValue = props[propKey];
|
|
587
|
+
let key = propKey;
|
|
588
|
+
if (is.colorSpaceExist(instance)) {
|
|
589
|
+
if (propKey === 'encoding') {
|
|
590
|
+
key = 'colorSpace';
|
|
875
591
|
}
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
});
|
|
884
|
-
if (!changed)
|
|
885
|
-
return;
|
|
886
|
-
selectionApi.update((prev) => [...prev, ...current]);
|
|
887
|
-
onCleanup(() => {
|
|
888
|
-
selectionApi.update((prev) => prev.filter((el) => !current.includes(el)));
|
|
889
|
-
});
|
|
890
|
-
});
|
|
592
|
+
else if (propKey === 'outputEncoding') {
|
|
593
|
+
key = 'outputColorSpace';
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
if (is.equ(propValue, instance[key]))
|
|
597
|
+
continue;
|
|
598
|
+
changes.push([propKey, propValue]);
|
|
891
599
|
}
|
|
892
|
-
|
|
893
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.11", type: NgtSelect, isStandalone: true, selector: "ngt-group[select], ngt-mesh[select]", inputs: { enabled: { classPropertyName: "enabled", publicName: "select", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
|
|
600
|
+
return changes;
|
|
894
601
|
}
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
capturedMap.delete(pointerId);
|
|
912
|
-
captureData.target.releasePointerCapture(pointerId);
|
|
913
|
-
}
|
|
602
|
+
// NOTE: this is a workaround to give the instance a chance to have the store from the parent.
|
|
603
|
+
// we clear this property after the applyProps is done
|
|
604
|
+
const NGT_APPLY_PROPS = '__ngt_apply_props__';
|
|
605
|
+
// https://github.com/mrdoob/three.js/pull/27042
|
|
606
|
+
// https://github.com/mrdoob/three.js/pull/22748
|
|
607
|
+
const colorMaps = ['map', 'emissiveMap', 'sheenColorMap', 'specularColorMap', 'envMap'];
|
|
608
|
+
function resolveInstanceKey(instance, key) {
|
|
609
|
+
let targetProp = instance[key];
|
|
610
|
+
if (!key.includes('.'))
|
|
611
|
+
return { root: instance, targetKey: key, targetProp };
|
|
612
|
+
// Resolve pierced target
|
|
613
|
+
targetProp = instance;
|
|
614
|
+
for (const part of key.split('.')) {
|
|
615
|
+
key = part;
|
|
616
|
+
instance = targetProp;
|
|
617
|
+
targetProp = targetProp[key];
|
|
914
618
|
}
|
|
619
|
+
// const chain = key.split('.');
|
|
620
|
+
// targetProp = chain.reduce((acc, part) => acc[part], instance);
|
|
621
|
+
// const targetKey = chain.pop()!;
|
|
622
|
+
//
|
|
623
|
+
// // Switch root if atomic
|
|
624
|
+
// if (!targetProp?.set) instance = chain.reduce((acc, part) => acc[part], instance);
|
|
625
|
+
return { root: instance, targetKey: key, targetProp };
|
|
915
626
|
}
|
|
916
|
-
function
|
|
917
|
-
|
|
918
|
-
//
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
627
|
+
// This function applies a set of changes to the instance
|
|
628
|
+
function applyProps(instance, props) {
|
|
629
|
+
// if props is empty
|
|
630
|
+
if (!Object.keys(props).length)
|
|
631
|
+
return instance;
|
|
632
|
+
const localState = getInstanceState(instance);
|
|
633
|
+
const rootState = localState?.store?.snapshot ?? instance[NGT_APPLY_PROPS]?.snapshot ?? {};
|
|
634
|
+
const changes = diffProps(instance, props);
|
|
635
|
+
for (let i = 0; i < changes.length; i++) {
|
|
636
|
+
let [key, value] = changes[i];
|
|
637
|
+
// Ignore setting undefined props
|
|
638
|
+
// https://github.com/pmndrs/react-three-fiber/issues/274
|
|
639
|
+
if (value === undefined)
|
|
640
|
+
continue;
|
|
641
|
+
// Alias (output)encoding => (output)colorSpace (since r152)
|
|
642
|
+
// https://github.com/pmndrs/react-three-fiber/pull/2829
|
|
643
|
+
// if (is.colorSpaceExist(instance)) {
|
|
644
|
+
// const sRGBEncoding = 3001;
|
|
645
|
+
// const SRGBColorSpace = 'srgb';
|
|
646
|
+
// const LinearSRGBColorSpace = 'srgb-linear';
|
|
647
|
+
//
|
|
648
|
+
// if (key === 'encoding') {
|
|
649
|
+
// key = 'colorSpace';
|
|
650
|
+
// value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
|
|
651
|
+
// } else if (key === 'outputEncoding') {
|
|
652
|
+
// key = 'outputColorSpace';
|
|
653
|
+
// value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
|
|
654
|
+
// }
|
|
655
|
+
// }
|
|
656
|
+
const { root, targetKey, targetProp } = resolveInstanceKey(instance, key);
|
|
657
|
+
// we have switched due to pierced props
|
|
658
|
+
if (root !== instance) {
|
|
659
|
+
return applyProps(root, { [targetKey]: value });
|
|
925
660
|
}
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
});
|
|
930
|
-
}
|
|
931
|
-
function createEvents(store) {
|
|
932
|
-
/** Calculates delta */
|
|
933
|
-
function calculateDistance(event) {
|
|
934
|
-
const internal = store.snapshot.internal;
|
|
935
|
-
const dx = event.offsetX - internal.initialClick[0];
|
|
936
|
-
const dy = event.offsetY - internal.initialClick[1];
|
|
937
|
-
return Math.round(Math.sqrt(dx * dx + dy * dy));
|
|
938
|
-
}
|
|
939
|
-
/** Returns true if an instance has a valid pointer-event registered, this excludes scroll, clicks etc */
|
|
940
|
-
function filterPointerEvents(objects) {
|
|
941
|
-
return objects.filter((obj) => ['move', 'over', 'enter', 'out', 'leave'].some((name) => {
|
|
942
|
-
const eventName = `pointer${name}`;
|
|
943
|
-
return getInstanceState(obj)?.handlers?.[eventName];
|
|
944
|
-
}));
|
|
945
|
-
}
|
|
946
|
-
function intersect(event, filter) {
|
|
947
|
-
const state = store.snapshot;
|
|
948
|
-
const duplicates = new Set();
|
|
949
|
-
const intersections = [];
|
|
950
|
-
// Allow callers to eliminate event objects
|
|
951
|
-
const eventsObjects = filter ? filter(state.internal.interaction) : state.internal.interaction;
|
|
952
|
-
// Reset all raycaster cameras to undefined
|
|
953
|
-
for (let i = 0; i < eventsObjects.length; i++) {
|
|
954
|
-
const objectRootState = getInstanceState(eventsObjects[i])?.store?.snapshot;
|
|
955
|
-
if (objectRootState) {
|
|
956
|
-
objectRootState.raycaster.camera = undefined;
|
|
957
|
-
}
|
|
661
|
+
// Layers have no copy function, we must therefore copy the mask property
|
|
662
|
+
if (targetProp instanceof THREE.Layers && value instanceof THREE.Layers) {
|
|
663
|
+
targetProp.mask = value.mask;
|
|
958
664
|
}
|
|
959
|
-
if (
|
|
960
|
-
|
|
961
|
-
state.events.compute?.(event, store, null);
|
|
665
|
+
else if (is.three(targetProp, 'isColor') && is.colorRepresentation(value)) {
|
|
666
|
+
targetProp.set(value);
|
|
962
667
|
}
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
//
|
|
970
|
-
if (
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
668
|
+
// Copy if properties match signatures
|
|
669
|
+
else if (targetProp &&
|
|
670
|
+
typeof targetProp.set === 'function' &&
|
|
671
|
+
typeof targetProp.copy === 'function' &&
|
|
672
|
+
value?.constructor &&
|
|
673
|
+
targetProp.constructor === value.constructor) {
|
|
674
|
+
// If both are geometries, we should assign the value directly instead of copying
|
|
675
|
+
if (is.three(targetProp, 'isBufferGeometry') &&
|
|
676
|
+
is.three(value, 'isBufferGeometry')) {
|
|
677
|
+
Object.assign(root, { [targetKey]: value });
|
|
678
|
+
}
|
|
679
|
+
else {
|
|
680
|
+
targetProp.copy(value);
|
|
975
681
|
}
|
|
976
|
-
// Intersect object by object
|
|
977
|
-
return objState.raycaster.camera ? objState.raycaster.intersectObject(obj, true) : [];
|
|
978
682
|
}
|
|
979
|
-
//
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
683
|
+
// Set array types
|
|
684
|
+
else if (targetProp && typeof targetProp.set === 'function' && Array.isArray(value)) {
|
|
685
|
+
if (typeof targetProp.fromArray === 'function')
|
|
686
|
+
targetProp.fromArray(value);
|
|
687
|
+
else
|
|
688
|
+
targetProp.set(...value);
|
|
689
|
+
}
|
|
690
|
+
// Set literal types
|
|
691
|
+
else if (targetProp && typeof targetProp.set === 'function' && typeof value !== 'object') {
|
|
692
|
+
const isColor = is.three(targetProp, 'isColor');
|
|
693
|
+
// Allow setting array scalars
|
|
694
|
+
if (!isColor && typeof targetProp.setScalar === 'function' && typeof value === 'number')
|
|
695
|
+
targetProp.setScalar(value);
|
|
696
|
+
// Otherwise just set single value
|
|
697
|
+
else
|
|
698
|
+
targetProp.set(value);
|
|
699
|
+
}
|
|
700
|
+
// Else, just overwrite the value
|
|
701
|
+
else {
|
|
702
|
+
Object.assign(root, { [targetKey]: value });
|
|
703
|
+
// Auto-convert sRGB texture parameters for built-in materials
|
|
704
|
+
// https://github.com/pmndrs/react-three-fiber/issues/344
|
|
705
|
+
// https://github.com/mrdoob/three.js/pull/25857
|
|
706
|
+
if (rootState &&
|
|
707
|
+
!rootState.linear &&
|
|
708
|
+
colorMaps.includes(targetKey) &&
|
|
709
|
+
root[targetKey]?.isTexture &&
|
|
710
|
+
// sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
|
|
711
|
+
root[targetKey].format === THREE.RGBAFormat &&
|
|
712
|
+
root[targetKey].type === THREE.UnsignedByteType) {
|
|
713
|
+
// NOTE: this cannot be set from the renderer (e.g. sRGB source textures rendered to P3)
|
|
714
|
+
root[targetKey].colorSpace = THREE.SRGBColorSpace;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
checkUpdate(root[targetKey]);
|
|
718
|
+
checkUpdate(targetProp);
|
|
719
|
+
invalidateInstance(instance);
|
|
720
|
+
}
|
|
721
|
+
const instanceHandlersCount = localState?.eventCount;
|
|
722
|
+
const parent = localState?.hierarchyStore?.snapshot.parent;
|
|
723
|
+
if (parent && rootState.internal && instance['raycast'] && instanceHandlersCount !== localState?.eventCount) {
|
|
724
|
+
// Pre-emptively remove the instance from the interaction manager
|
|
725
|
+
const index = rootState.internal.interaction.indexOf(instance);
|
|
726
|
+
if (index > -1)
|
|
727
|
+
rootState.internal.interaction.splice(index, 1);
|
|
728
|
+
// Add the instance to the interaction manager only when it has handlers
|
|
729
|
+
if (localState?.eventCount)
|
|
730
|
+
rootState.internal.interaction.push(instance);
|
|
731
|
+
}
|
|
732
|
+
if (parent && localState?.onUpdate && changes.length) {
|
|
733
|
+
localState.onUpdate(instance);
|
|
734
|
+
}
|
|
735
|
+
// clearing the intermediate store from the instance
|
|
736
|
+
if (instance[NGT_APPLY_PROPS])
|
|
737
|
+
delete instance[NGT_APPLY_PROPS];
|
|
738
|
+
return instance;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
const catalogue = {};
|
|
742
|
+
function extend(objects) {
|
|
743
|
+
const keys = Object.keys(objects);
|
|
744
|
+
Object.assign(catalogue, objects);
|
|
745
|
+
return () => {
|
|
746
|
+
remove(...keys);
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
function remove(...keys) {
|
|
750
|
+
for (const key of keys) {
|
|
751
|
+
delete catalogue[key];
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
const NGT_CATALOGUE = new InjectionToken('NGT_CATALOGUE', { factory: () => catalogue });
|
|
755
|
+
function injectCatalogue() {
|
|
756
|
+
return inject(NGT_CATALOGUE);
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
function isRendererNode(node) {
|
|
760
|
+
return !!node && typeof node === 'object' && NGT_RENDERER_NODE_FLAG in node;
|
|
761
|
+
}
|
|
762
|
+
function createRendererNode(type, node, document) {
|
|
763
|
+
const state = [type, false, undefined, undefined, undefined, undefined, []];
|
|
764
|
+
const rendererNode = Object.assign(node, { [NGT_RENDERER_NODE_FLAG]: state });
|
|
765
|
+
// NOTE: assign ownerDocument to node so we can use HostListener in Component
|
|
766
|
+
if (!rendererNode['ownerDocument'])
|
|
767
|
+
rendererNode['ownerDocument'] = document;
|
|
768
|
+
// NOTE: Angular SSR calls `node.getAttribute()` to retrieve hydration info on a node
|
|
769
|
+
if (!('getAttribute' in rendererNode) || typeof rendererNode['getAttribute'] !== 'function') {
|
|
770
|
+
const getNodeAttribute = (name) => rendererNode[name];
|
|
771
|
+
getNodeAttribute[NGT_GET_NODE_ATTRIBUTE_FLAG] = true;
|
|
772
|
+
Object.defineProperty(rendererNode, 'getAttribute', { value: getNodeAttribute, configurable: true });
|
|
773
|
+
}
|
|
774
|
+
return rendererNode;
|
|
775
|
+
}
|
|
776
|
+
function setRendererParentNode(node, parent) {
|
|
777
|
+
if (!node.__ngt_renderer__[5 /* NgtRendererClassId.parent */]) {
|
|
778
|
+
node.__ngt_renderer__[5 /* NgtRendererClassId.parent */] = parent;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
function addRendererChildNode(node, child) {
|
|
782
|
+
if (!node.__ngt_renderer__[6 /* NgtRendererClassId.children */].includes(child)) {
|
|
783
|
+
node.__ngt_renderer__[6 /* NgtRendererClassId.children */].push(child);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
const idCache = {};
|
|
788
|
+
function makeId(event) {
|
|
789
|
+
if (event) {
|
|
790
|
+
return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
|
|
791
|
+
}
|
|
792
|
+
const newId = THREE.MathUtils.generateUUID();
|
|
793
|
+
// ensure not already used
|
|
794
|
+
if (!idCache[newId]) {
|
|
795
|
+
idCache[newId] = true;
|
|
796
|
+
return newId;
|
|
797
|
+
}
|
|
798
|
+
return makeId();
|
|
799
|
+
}
|
|
800
|
+
function makeDpr(dpr, window) {
|
|
801
|
+
// Err on the side of progress by assuming 2x dpr if we can't detect it
|
|
802
|
+
// This will happen in workers where window is defined but dpr isn't.
|
|
803
|
+
const target = typeof window !== 'undefined' ? (window.devicePixelRatio ?? 2) : 1;
|
|
804
|
+
return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], target), dpr[1]) : dpr;
|
|
805
|
+
}
|
|
806
|
+
function makeRendererInstance(glOptions, canvas) {
|
|
807
|
+
const defaultOptions = {
|
|
808
|
+
powerPreference: 'high-performance',
|
|
809
|
+
canvas,
|
|
810
|
+
antialias: true,
|
|
811
|
+
alpha: true,
|
|
812
|
+
};
|
|
813
|
+
const customRenderer = (typeof glOptions === 'function' ? glOptions(defaultOptions) : glOptions);
|
|
814
|
+
if (is.renderer(customRenderer))
|
|
815
|
+
return customRenderer;
|
|
816
|
+
return new THREE.WebGLRenderer({ ...defaultOptions, ...glOptions });
|
|
817
|
+
}
|
|
818
|
+
function makeCameraInstance(isOrthographic, size) {
|
|
819
|
+
if (isOrthographic)
|
|
820
|
+
return new THREE.OrthographicCamera(0, 0, 0, 0, 0.1, 1000);
|
|
821
|
+
return new THREE.PerspectiveCamera(75, size.width / size.height, 0.1, 1000);
|
|
822
|
+
}
|
|
823
|
+
function makeObjectGraph(object) {
|
|
824
|
+
const data = { nodes: {}, materials: {}, meshes: {} };
|
|
825
|
+
if (object) {
|
|
826
|
+
object.traverse((child) => {
|
|
827
|
+
if (child.name)
|
|
828
|
+
data.nodes[child.name] = child;
|
|
829
|
+
if ('material' in child && !data.materials[child.material.name]) {
|
|
830
|
+
data.materials[child.material.name] = child
|
|
831
|
+
.material;
|
|
832
|
+
}
|
|
833
|
+
if (is.three(child, 'isMesh') && !data.meshes[child.name])
|
|
834
|
+
data.meshes[child.name] = child;
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
return data;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
/**
|
|
841
|
+
* Release pointer captures.
|
|
842
|
+
* This is called by releasePointerCapture in the API, and when an object is removed.
|
|
843
|
+
*/
|
|
844
|
+
function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
|
|
845
|
+
const captureData = captures.get(obj);
|
|
846
|
+
if (captureData) {
|
|
847
|
+
captures.delete(obj);
|
|
848
|
+
// If this was the last capturing object for this pointer
|
|
849
|
+
if (captures.size === 0) {
|
|
850
|
+
capturedMap.delete(pointerId);
|
|
851
|
+
captureData.target.releasePointerCapture(pointerId);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
function removeInteractivity(store, object) {
|
|
856
|
+
const { internal } = store.snapshot;
|
|
857
|
+
// Removes every trace of an object from the data store
|
|
858
|
+
internal.interaction = internal.interaction.filter((o) => o !== object);
|
|
859
|
+
internal.initialHits = internal.initialHits.filter((o) => o !== object);
|
|
860
|
+
internal.hovered.forEach((value, key) => {
|
|
861
|
+
if (value.eventObject === object || value.object === object) {
|
|
862
|
+
// Clear out intersects, they are outdated by now
|
|
863
|
+
internal.hovered.delete(key);
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
internal.capturedMap.forEach((captures, pointerId) => {
|
|
867
|
+
releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
function createEvents(store) {
|
|
871
|
+
/** Calculates delta */
|
|
872
|
+
function calculateDistance(event) {
|
|
873
|
+
const internal = store.snapshot.internal;
|
|
874
|
+
const dx = event.offsetX - internal.initialClick[0];
|
|
875
|
+
const dy = event.offsetY - internal.initialClick[1];
|
|
876
|
+
return Math.round(Math.sqrt(dx * dx + dy * dy));
|
|
877
|
+
}
|
|
878
|
+
/** Returns true if an instance has a valid pointer-event registered, this excludes scroll, clicks etc */
|
|
879
|
+
function filterPointerEvents(objects) {
|
|
880
|
+
return objects.filter((obj) => ['move', 'over', 'enter', 'out', 'leave'].some((name) => {
|
|
881
|
+
const eventName = `pointer${name}`;
|
|
882
|
+
return getInstanceState(obj)?.handlers?.[eventName];
|
|
883
|
+
}));
|
|
884
|
+
}
|
|
885
|
+
function intersect(event, filter) {
|
|
886
|
+
const state = store.snapshot;
|
|
887
|
+
const duplicates = new Set();
|
|
888
|
+
const intersections = [];
|
|
889
|
+
// Allow callers to eliminate event objects
|
|
890
|
+
const eventsObjects = filter ? filter(state.internal.interaction) : state.internal.interaction;
|
|
891
|
+
// Reset all raycaster cameras to undefined
|
|
892
|
+
for (let i = 0; i < eventsObjects.length; i++) {
|
|
893
|
+
const objectRootState = getInstanceState(eventsObjects[i])?.store?.snapshot;
|
|
894
|
+
if (objectRootState) {
|
|
895
|
+
objectRootState.raycaster.camera = undefined;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
if (!state.previousRoot) {
|
|
899
|
+
// Make sure root-level pointer and ray are set up
|
|
900
|
+
state.events.compute?.(event, store, null);
|
|
901
|
+
}
|
|
902
|
+
function handleRaycast(obj) {
|
|
903
|
+
const objStore = getInstanceState(obj)?.store;
|
|
904
|
+
const objState = objStore?.snapshot;
|
|
905
|
+
// Skip event handling when noEvents is set, or when the raycasters camera is null
|
|
906
|
+
if (!objState || !objState.events.enabled || objState.raycaster.camera === null)
|
|
907
|
+
return [];
|
|
908
|
+
// When the camera is undefined we have to call the event layers update function
|
|
909
|
+
if (objState.raycaster.camera === undefined) {
|
|
910
|
+
objState.events.compute?.(event, objStore, objState.previousRoot);
|
|
911
|
+
// If the camera is still undefined we have to skip this layer entirely
|
|
912
|
+
if (objState.raycaster.camera === undefined)
|
|
913
|
+
objState.raycaster.camera = null;
|
|
914
|
+
}
|
|
915
|
+
// Intersect object by object
|
|
916
|
+
return objState.raycaster.camera ? objState.raycaster.intersectObject(obj, true) : [];
|
|
917
|
+
}
|
|
918
|
+
// Collect events
|
|
919
|
+
let hits = eventsObjects
|
|
920
|
+
// Intersect objects
|
|
921
|
+
.flatMap(handleRaycast)
|
|
922
|
+
// Sort by event priority and distance
|
|
923
|
+
.sort((a, b) => {
|
|
924
|
+
const aState = getInstanceState(a.object)?.store?.snapshot;
|
|
925
|
+
const bState = getInstanceState(b.object)?.store?.snapshot;
|
|
926
|
+
if (!aState || !bState)
|
|
927
|
+
return a.distance - b.distance;
|
|
928
|
+
return bState.events.priority - aState.events.priority || a.distance - b.distance;
|
|
929
|
+
})
|
|
930
|
+
// Filter out duplicates
|
|
931
|
+
.filter((item) => {
|
|
932
|
+
const id = makeId(item);
|
|
994
933
|
if (duplicates.has(id))
|
|
995
934
|
return false;
|
|
996
935
|
duplicates.add(id);
|
|
@@ -1172,6 +1111,9 @@ function createEvents(store) {
|
|
|
1172
1111
|
}
|
|
1173
1112
|
};
|
|
1174
1113
|
}
|
|
1114
|
+
const isPointerMove = name === 'pointermove';
|
|
1115
|
+
const isClickEvent = name === 'click' || name === 'contextmenu' || name === 'dblclick';
|
|
1116
|
+
const filter = isPointerMove ? filterPointerEvents : undefined;
|
|
1175
1117
|
// Any other pointer goes here ...
|
|
1176
1118
|
return function handleEvent(event) {
|
|
1177
1119
|
// NOTE: __pointerMissed$ on NgtStore is private subject since we only expose the Observable
|
|
@@ -1180,9 +1122,6 @@ function createEvents(store) {
|
|
|
1180
1122
|
// prepareRay(event)
|
|
1181
1123
|
internal.lastEvent.nativeElement = event;
|
|
1182
1124
|
// Get fresh intersects
|
|
1183
|
-
const isPointerMove = name === 'pointermove';
|
|
1184
|
-
const isClickEvent = name === 'click' || name === 'contextmenu' || name === 'dblclick';
|
|
1185
|
-
const filter = isPointerMove ? filterPointerEvents : undefined;
|
|
1186
1125
|
const hits = intersect(event, filter);
|
|
1187
1126
|
const delta = isClickEvent ? calculateDistance(event) : 0;
|
|
1188
1127
|
// Save initial coordinates on pointer-down
|
|
@@ -1272,1323 +1211,532 @@ function createEvents(store) {
|
|
|
1272
1211
|
return { handlePointer };
|
|
1273
1212
|
}
|
|
1274
1213
|
|
|
1275
|
-
|
|
1276
|
-
const
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1214
|
+
function attach(object, value, paths = [], useApplyProps = false) {
|
|
1215
|
+
const [base, ...remaining] = paths;
|
|
1216
|
+
if (!base)
|
|
1217
|
+
return;
|
|
1218
|
+
if (remaining.length === 0) {
|
|
1219
|
+
if (useApplyProps)
|
|
1220
|
+
applyProps(object, { [base]: value });
|
|
1221
|
+
else
|
|
1222
|
+
object[base] = value;
|
|
1283
1223
|
}
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
static { _a = NGT_HTML_FLAG; }
|
|
1288
|
-
static { this[_a] = true; }
|
|
1289
|
-
constructor() {
|
|
1290
|
-
this.domElement = inject(NGT_HTML_DOM_ELEMENT, { self: true, optional: true });
|
|
1291
|
-
const host = inject(ElementRef);
|
|
1292
|
-
const store = injectStore();
|
|
1293
|
-
if (this.domElement === 'gl') {
|
|
1294
|
-
Object.assign(host.nativeElement, {
|
|
1295
|
-
[NGT_DOM_PARENT_FLAG]: store.snapshot.gl.domElement.parentElement,
|
|
1296
|
-
});
|
|
1297
|
-
}
|
|
1298
|
-
else if (this.domElement) {
|
|
1299
|
-
Object.assign(host.nativeElement, { [NGT_DOM_PARENT_FLAG]: this.domElement });
|
|
1300
|
-
}
|
|
1301
|
-
inject(DestroyRef).onDestroy(() => {
|
|
1302
|
-
host.nativeElement[NGT_DOM_PARENT_FLAG] = null;
|
|
1303
|
-
delete host.nativeElement[NGT_DOM_PARENT_FLAG];
|
|
1304
|
-
});
|
|
1224
|
+
else {
|
|
1225
|
+
assignEmpty(object, base, useApplyProps);
|
|
1226
|
+
attach(object[base], value, remaining, useApplyProps);
|
|
1305
1227
|
}
|
|
1306
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgtHTML, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1307
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.11", type: NgtHTML, isStandalone: true, ngImport: i0 }); }
|
|
1308
1228
|
}
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
let urls = [];
|
|
1317
|
-
if (Array.isArray(input)) {
|
|
1318
|
-
urls = input;
|
|
1229
|
+
function detach(parent, child, attachProp) {
|
|
1230
|
+
const childInstanceState = getInstanceState(child);
|
|
1231
|
+
if (childInstanceState) {
|
|
1232
|
+
if (Array.isArray(attachProp))
|
|
1233
|
+
attach(parent, childInstanceState.previousAttach, attachProp, childInstanceState.type === 'ngt-value');
|
|
1234
|
+
else
|
|
1235
|
+
childInstanceState.previousAttach?.();
|
|
1319
1236
|
}
|
|
1320
|
-
|
|
1321
|
-
|
|
1237
|
+
}
|
|
1238
|
+
function assignEmpty(obj, base, shouldAssignStoreForApplyProps = false) {
|
|
1239
|
+
if ((!Object.hasOwn(obj, base) && Reflect && !!Reflect.has && !Reflect.has(obj, base)) || obj[base] === undefined) {
|
|
1240
|
+
obj[base] = {};
|
|
1322
1241
|
}
|
|
1323
|
-
|
|
1324
|
-
|
|
1242
|
+
if (shouldAssignStoreForApplyProps) {
|
|
1243
|
+
const instanceState = getInstanceState(obj[base]);
|
|
1244
|
+
// if we already have instance state, bail out
|
|
1245
|
+
if (instanceState)
|
|
1246
|
+
return;
|
|
1247
|
+
const parentInstanceState = getInstanceState(obj);
|
|
1248
|
+
// if parent doesn't have instance state, bail out
|
|
1249
|
+
if (!parentInstanceState)
|
|
1250
|
+
return;
|
|
1251
|
+
Object.assign(obj[base], { [NGT_APPLY_PROPS]: parentInstanceState.store });
|
|
1325
1252
|
}
|
|
1326
|
-
return urls.map((url) => (url.includes('undefined') || url.includes('null') || !url ? '' : url));
|
|
1327
|
-
}
|
|
1328
|
-
function load(loaderConstructorFactory, inputs, { extensions, onLoad, onProgress, } = {}) {
|
|
1329
|
-
return () => {
|
|
1330
|
-
const urls = normalizeInputs$1(inputs());
|
|
1331
|
-
let loader = memoizedLoaders$1.get(loaderConstructorFactory(urls));
|
|
1332
|
-
if (!loader) {
|
|
1333
|
-
loader = new (loaderConstructorFactory(urls))();
|
|
1334
|
-
memoizedLoaders$1.set(loaderConstructorFactory(urls), loader);
|
|
1335
|
-
}
|
|
1336
|
-
if (extensions)
|
|
1337
|
-
extensions(loader);
|
|
1338
|
-
return urls.map((url) => {
|
|
1339
|
-
if (url === '')
|
|
1340
|
-
return Promise.resolve(null);
|
|
1341
|
-
if (!cached$1.has(url)) {
|
|
1342
|
-
cached$1.set(url, new Promise((resolve, reject) => {
|
|
1343
|
-
loader.load(url, (data) => {
|
|
1344
|
-
if ('scene' in data) {
|
|
1345
|
-
Object.assign(data, makeObjectGraph(data['scene']));
|
|
1346
|
-
}
|
|
1347
|
-
if (onLoad) {
|
|
1348
|
-
onLoad(data);
|
|
1349
|
-
}
|
|
1350
|
-
resolve(data);
|
|
1351
|
-
}, onProgress, (error) => reject(new Error(`[NGT] Could not load ${url}: ${error?.message}`)));
|
|
1352
|
-
}));
|
|
1353
|
-
}
|
|
1354
|
-
return cached$1.get(url);
|
|
1355
|
-
});
|
|
1356
|
-
};
|
|
1357
1253
|
}
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
* @since v4.0.0~
|
|
1361
|
-
*/
|
|
1362
|
-
function _injectLoader(loaderConstructorFactory, inputs, { extensions, onProgress, onLoad, injector, } = {}) {
|
|
1363
|
-
return assertInjector(_injectLoader, injector, () => {
|
|
1364
|
-
const response = signal(null);
|
|
1365
|
-
const cachedResultPromisesEffect = load(loaderConstructorFactory, inputs, {
|
|
1366
|
-
extensions,
|
|
1367
|
-
onProgress,
|
|
1368
|
-
onLoad: onLoad,
|
|
1369
|
-
});
|
|
1370
|
-
effect(() => {
|
|
1371
|
-
const originalUrls = inputs();
|
|
1372
|
-
const cachedResultPromises = cachedResultPromisesEffect();
|
|
1373
|
-
Promise.all(cachedResultPromises).then((results) => {
|
|
1374
|
-
response.update(() => {
|
|
1375
|
-
if (Array.isArray(originalUrls))
|
|
1376
|
-
return results;
|
|
1377
|
-
if (typeof originalUrls === 'string')
|
|
1378
|
-
return results[0];
|
|
1379
|
-
const keys = Object.keys(originalUrls);
|
|
1380
|
-
return keys.reduce((result, key) => {
|
|
1381
|
-
// @ts-ignore
|
|
1382
|
-
result[key] = results[keys.indexOf(key)];
|
|
1383
|
-
return result;
|
|
1384
|
-
}, {});
|
|
1385
|
-
});
|
|
1386
|
-
});
|
|
1387
|
-
});
|
|
1388
|
-
return response.asReadonly();
|
|
1389
|
-
});
|
|
1254
|
+
function createAttachFunction(cb) {
|
|
1255
|
+
return (parent, child, store) => cb({ parent, child, store });
|
|
1390
1256
|
}
|
|
1391
|
-
_injectLoader.preload = (loaderConstructorFactory, inputs, extensions, onLoad) => {
|
|
1392
|
-
const effects = load(loaderConstructorFactory, inputs, { extensions, onLoad })();
|
|
1393
|
-
if (effects) {
|
|
1394
|
-
void Promise.all(effects);
|
|
1395
|
-
}
|
|
1396
|
-
};
|
|
1397
|
-
_injectLoader.destroy = () => {
|
|
1398
|
-
cached$1.clear();
|
|
1399
|
-
};
|
|
1400
|
-
_injectLoader.clear = (urls) => {
|
|
1401
|
-
const urlToClear = Array.isArray(urls) ? urls : [urls];
|
|
1402
|
-
urlToClear.forEach((url) => {
|
|
1403
|
-
cached$1.delete(url);
|
|
1404
|
-
});
|
|
1405
|
-
};
|
|
1406
|
-
/**
|
|
1407
|
-
* @deprecated Use loaderResource instead. Will be removed in v5.0.0
|
|
1408
|
-
* @since v4.0.0~
|
|
1409
|
-
*/
|
|
1410
|
-
const injectLoader = _injectLoader;
|
|
1411
1257
|
|
|
1412
|
-
function
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1258
|
+
function kebabToPascal(str) {
|
|
1259
|
+
if (!str)
|
|
1260
|
+
return str; // Handle empty input
|
|
1261
|
+
let pascalStr = '';
|
|
1262
|
+
let capitalizeNext = true; // Flag to track capitalization
|
|
1263
|
+
for (let i = 0; i < str.length; i++) {
|
|
1264
|
+
const char = str[i];
|
|
1265
|
+
if (char === '-') {
|
|
1266
|
+
capitalizeNext = true;
|
|
1267
|
+
continue;
|
|
1268
|
+
}
|
|
1269
|
+
pascalStr += capitalizeNext ? char.toUpperCase() : char;
|
|
1270
|
+
capitalizeNext = false;
|
|
1422
1271
|
}
|
|
1423
|
-
return
|
|
1272
|
+
return pascalStr;
|
|
1424
1273
|
}
|
|
1425
|
-
|
|
1426
|
-
const
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1274
|
+
function propagateStoreRecursively(node, parentNode) {
|
|
1275
|
+
const iS = getInstanceState(node);
|
|
1276
|
+
const pIS = getInstanceState(parentNode);
|
|
1277
|
+
if (!iS || !pIS)
|
|
1278
|
+
return;
|
|
1279
|
+
// assign store on child if not already exist
|
|
1280
|
+
// or child store is not the same as parent store
|
|
1281
|
+
// or child store is the parent of parent store
|
|
1282
|
+
if (!iS.store || iS.store !== pIS.store || iS.store === pIS.store.snapshot.previousRoot) {
|
|
1283
|
+
iS.store = pIS.store;
|
|
1284
|
+
// Call addInteraction if it exists
|
|
1285
|
+
iS.addInteraction?.(pIS.store);
|
|
1286
|
+
// Collect all children (objects and nonObjects)
|
|
1287
|
+
const children = [
|
|
1288
|
+
...(iS.objects ? untracked(iS.objects) : []),
|
|
1289
|
+
...(iS.nonObjects ? untracked(iS.nonObjects) : []),
|
|
1290
|
+
];
|
|
1291
|
+
// Recursively reassign the store for each child
|
|
1292
|
+
for (const child of children) {
|
|
1293
|
+
propagateStoreRecursively(child, node);
|
|
1294
|
+
}
|
|
1435
1295
|
}
|
|
1436
|
-
if (extensions)
|
|
1437
|
-
extensions(loader);
|
|
1438
|
-
return { urls, normalizedUrls, loader };
|
|
1439
1296
|
}
|
|
1440
|
-
function
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1297
|
+
function attachThreeNodes(parent, child) {
|
|
1298
|
+
const pIS = getInstanceState(parent);
|
|
1299
|
+
const cIS = getInstanceState(child);
|
|
1300
|
+
if (!pIS || !cIS) {
|
|
1301
|
+
throw new Error(`[NGT] THREE instances need to be prepared with local state.`);
|
|
1302
|
+
}
|
|
1303
|
+
// whether the child is added to the parent with parent.add()
|
|
1304
|
+
let added = false;
|
|
1305
|
+
// propagate store recursively
|
|
1306
|
+
propagateStoreRecursively(child, parent);
|
|
1307
|
+
if (cIS.attach) {
|
|
1308
|
+
const attachProp = cIS.attach;
|
|
1309
|
+
if (typeof attachProp === 'function') {
|
|
1310
|
+
let attachCleanUp = undefined;
|
|
1311
|
+
if (cIS.type === 'ngt-value') {
|
|
1312
|
+
if (cIS.hierarchyStore.snapshot.parent !== parent) {
|
|
1313
|
+
cIS.setParent(parent);
|
|
1451
1314
|
}
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
return promise;
|
|
1457
|
-
});
|
|
1458
|
-
}
|
|
1459
|
-
function loaderResource(loaderConstructorFactory, input, { extensions, onLoad, onProgress, injector, } = {}) {
|
|
1460
|
-
return assertInjector(loaderResource, injector, () => {
|
|
1461
|
-
return resource({
|
|
1462
|
-
request: () => getLoaderRequestParams(input, loaderConstructorFactory, extensions),
|
|
1463
|
-
loader: async ({ request }) => {
|
|
1464
|
-
// TODO: use the abortSignal when THREE.Loader supports it
|
|
1465
|
-
const loadedResults = await Promise.all(getLoaderPromises(request, onProgress));
|
|
1466
|
-
let results;
|
|
1467
|
-
if (Array.isArray(request.urls)) {
|
|
1468
|
-
results = loadedResults;
|
|
1469
|
-
}
|
|
1470
|
-
else if (typeof request.urls === 'string') {
|
|
1471
|
-
results = loadedResults[0];
|
|
1472
|
-
}
|
|
1473
|
-
else {
|
|
1474
|
-
const keys = Object.keys(request.urls);
|
|
1475
|
-
results = keys.reduce((result, key) => {
|
|
1476
|
-
// @ts-ignore
|
|
1477
|
-
result[key] = loadedResults[keys.indexOf(key)];
|
|
1478
|
-
return result;
|
|
1479
|
-
}, {});
|
|
1480
|
-
}
|
|
1481
|
-
if (onLoad)
|
|
1482
|
-
onLoad(results);
|
|
1483
|
-
return results;
|
|
1484
|
-
},
|
|
1485
|
-
});
|
|
1486
|
-
});
|
|
1487
|
-
}
|
|
1488
|
-
loaderResource.preload = (loaderConstructor, inputs, extensions) => {
|
|
1489
|
-
const params = getLoaderRequestParams(() => inputs, () => loaderConstructor, extensions);
|
|
1490
|
-
void Promise.all(getLoaderPromises(params));
|
|
1491
|
-
};
|
|
1492
|
-
loaderResource.destroy = () => {
|
|
1493
|
-
cached.clear();
|
|
1494
|
-
};
|
|
1495
|
-
loaderResource.clear = (urls) => {
|
|
1496
|
-
const urlToClear = Array.isArray(urls) ? urls : [urls];
|
|
1497
|
-
urlToClear.forEach((url) => {
|
|
1498
|
-
cached.delete(url);
|
|
1499
|
-
});
|
|
1500
|
-
};
|
|
1501
|
-
|
|
1502
|
-
const RGBA_REGEX = /rgba?\((\d+),\s*(\d+),\s*(\d+),?\s*(\d*\.?\d+)?\)/;
|
|
1503
|
-
const DEFAULT_COLOR = 0x000000;
|
|
1504
|
-
class NgtHexify {
|
|
1505
|
-
constructor() {
|
|
1506
|
-
this.document = inject(DOCUMENT, { optional: true });
|
|
1507
|
-
this.cache = {};
|
|
1508
|
-
}
|
|
1509
|
-
/**
|
|
1510
|
-
* transforms a:
|
|
1511
|
-
* - hex string to a hex number
|
|
1512
|
-
* - rgb string to a hex number
|
|
1513
|
-
* - rgba string to a hex number
|
|
1514
|
-
* - css color string to a hex number
|
|
1515
|
-
*
|
|
1516
|
-
* always default to black if failed
|
|
1517
|
-
* @param value
|
|
1518
|
-
*/
|
|
1519
|
-
transform(value) {
|
|
1520
|
-
if (value == null)
|
|
1521
|
-
return DEFAULT_COLOR;
|
|
1522
|
-
if (value.startsWith('#')) {
|
|
1523
|
-
if (!this.cache[value]) {
|
|
1524
|
-
this.cache[value] = this.hexStringToNumber(value);
|
|
1315
|
+
// at this point we don't have rawValue yet, so we bail and wait until the Renderer recalls attach
|
|
1316
|
+
if (child.__ngt_renderer__[2 /* NgtRendererClassId.rawValue */] === undefined)
|
|
1317
|
+
return;
|
|
1318
|
+
attachCleanUp = attachProp(parent, child.__ngt_renderer__[2 /* NgtRendererClassId.rawValue */], cIS.store);
|
|
1525
1319
|
}
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
if (!this.ctx) {
|
|
1529
|
-
this.ctx = this.document?.createElement('canvas').getContext('2d');
|
|
1530
|
-
}
|
|
1531
|
-
if (!this.ctx) {
|
|
1532
|
-
console.warn('[NGT] hexify: canvas context is not available');
|
|
1533
|
-
return DEFAULT_COLOR;
|
|
1534
|
-
}
|
|
1535
|
-
this.ctx.fillStyle = value;
|
|
1536
|
-
const computedValue = this.ctx.fillStyle;
|
|
1537
|
-
if (computedValue.startsWith('#')) {
|
|
1538
|
-
if (!this.cache[computedValue]) {
|
|
1539
|
-
this.cache[computedValue] = this.hexStringToNumber(computedValue);
|
|
1320
|
+
else {
|
|
1321
|
+
attachCleanUp = attachProp(parent, child, cIS.store);
|
|
1540
1322
|
}
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
if (!computedValue.startsWith('rgba')) {
|
|
1544
|
-
console.warn(`[NGT] hexify: invalid color format. Expected rgba or hex, receive: ${computedValue}`);
|
|
1545
|
-
return DEFAULT_COLOR;
|
|
1546
|
-
}
|
|
1547
|
-
const match = computedValue.match(RGBA_REGEX);
|
|
1548
|
-
if (!match) {
|
|
1549
|
-
console.warn(`[NGT] hexify: invalid color format. Expected rgba or hex, receive: ${computedValue}`);
|
|
1550
|
-
return DEFAULT_COLOR;
|
|
1323
|
+
if (attachCleanUp)
|
|
1324
|
+
cIS.previousAttach = attachCleanUp;
|
|
1551
1325
|
}
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1326
|
+
else {
|
|
1327
|
+
// we skip attach none if set explicitly
|
|
1328
|
+
if (attachProp[0] === 'none') {
|
|
1329
|
+
invalidateInstance(child);
|
|
1330
|
+
return;
|
|
1331
|
+
}
|
|
1332
|
+
// handle material array
|
|
1333
|
+
if (attachProp[0] === 'material' &&
|
|
1334
|
+
attachProp[1] &&
|
|
1335
|
+
typeof Number(attachProp[1]) === 'number' &&
|
|
1336
|
+
is.three(child, 'isMaterial') &&
|
|
1337
|
+
!Array.isArray(parent['material'])) {
|
|
1338
|
+
parent['material'] = [];
|
|
1339
|
+
}
|
|
1340
|
+
if (cIS.type === 'ngt-value') {
|
|
1341
|
+
if (cIS.hierarchyStore.snapshot.parent !== parent) {
|
|
1342
|
+
cIS.setParent(parent);
|
|
1343
|
+
}
|
|
1344
|
+
// at this point we don't have rawValue yet, so we bail and wait until the Renderer recalls attach
|
|
1345
|
+
if (child.__ngt_renderer__[2 /* NgtRendererClassId.rawValue */] === undefined)
|
|
1346
|
+
return;
|
|
1347
|
+
// save prev value
|
|
1348
|
+
cIS.previousAttach = attachProp.reduce((value, key) => value[key], parent);
|
|
1349
|
+
attach(parent, child.__ngt_renderer__[2 /* NgtRendererClassId.rawValue */], attachProp, true);
|
|
1350
|
+
}
|
|
1351
|
+
else {
|
|
1352
|
+
// save prev value
|
|
1353
|
+
cIS.previousAttach = attachProp.reduce((value, key) => value[key], parent);
|
|
1354
|
+
attach(parent, child, attachProp);
|
|
1355
|
+
}
|
|
1567
1356
|
}
|
|
1568
|
-
return this.cache[cacheKey];
|
|
1569
1357
|
}
|
|
1570
|
-
|
|
1571
|
-
|
|
1358
|
+
else if (is.three(parent, 'isObject3D') && is.three(child, 'isObject3D')) {
|
|
1359
|
+
parent.add(child);
|
|
1360
|
+
added = true;
|
|
1361
|
+
cIS.addInteraction?.(cIS.store || pIS.store);
|
|
1572
1362
|
}
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
return hex.length === 1 ? '0' + hex : hex;
|
|
1363
|
+
if (pIS.add) {
|
|
1364
|
+
pIS.add(child, added ? 'objects' : 'nonObjects');
|
|
1576
1365
|
}
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
}
|
|
1580
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgtHexify, decorators: [{
|
|
1581
|
-
type: Pipe,
|
|
1582
|
-
args: [{ name: 'hexify', pure: true }]
|
|
1583
|
-
}] });
|
|
1584
|
-
|
|
1585
|
-
const catalogue = {};
|
|
1586
|
-
function extend(objects) {
|
|
1587
|
-
const keys = Object.keys(objects);
|
|
1588
|
-
Object.assign(catalogue, objects);
|
|
1589
|
-
return () => {
|
|
1590
|
-
remove(...keys);
|
|
1591
|
-
};
|
|
1592
|
-
}
|
|
1593
|
-
function remove(...keys) {
|
|
1594
|
-
for (const key of keys) {
|
|
1595
|
-
delete catalogue[key];
|
|
1366
|
+
if (cIS.parent && untracked(cIS.parent) !== parent) {
|
|
1367
|
+
cIS.setParent(parent);
|
|
1596
1368
|
}
|
|
1369
|
+
// NOTE: this does not mean that the child is actually attached to the parent on the scenegraph.
|
|
1370
|
+
// a child on the Angular template can also emit onAttach
|
|
1371
|
+
if (cIS.onAttach)
|
|
1372
|
+
cIS.onAttach({ parent, node: child });
|
|
1373
|
+
invalidateInstance(child);
|
|
1374
|
+
invalidateInstance(parent);
|
|
1597
1375
|
}
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
if (keysToOmit.includes(key))
|
|
1609
|
-
continue;
|
|
1610
|
-
Object.assign(result, { [key]: obj[key] });
|
|
1611
|
-
}
|
|
1612
|
-
return result;
|
|
1613
|
-
}, { equal });
|
|
1614
|
-
}
|
|
1615
|
-
function pick(objFn, keyOrKeys, equal) {
|
|
1616
|
-
if (Array.isArray(keyOrKeys)) {
|
|
1617
|
-
if (!equal) {
|
|
1618
|
-
equal = (a, b) => is.equ(a, b, { objects: 'shallow', arrays: 'shallow' });
|
|
1619
|
-
}
|
|
1620
|
-
return computed(() => {
|
|
1621
|
-
const obj = objFn();
|
|
1622
|
-
const result = {};
|
|
1623
|
-
for (const key of keyOrKeys) {
|
|
1624
|
-
if (!(key in obj))
|
|
1625
|
-
continue;
|
|
1626
|
-
Object.assign(result, { [key]: obj[key] });
|
|
1627
|
-
}
|
|
1628
|
-
return result;
|
|
1629
|
-
}, { equal });
|
|
1376
|
+
function removeThreeChild(child, parent, dispose) {
|
|
1377
|
+
const pIS = getInstanceState(parent);
|
|
1378
|
+
const cIS = getInstanceState(child);
|
|
1379
|
+
// clear parent ref
|
|
1380
|
+
cIS?.setParent(null);
|
|
1381
|
+
// remove child from parent
|
|
1382
|
+
pIS?.remove?.(child, 'objects');
|
|
1383
|
+
pIS?.remove?.(child, 'nonObjects');
|
|
1384
|
+
if (cIS?.attach) {
|
|
1385
|
+
detach(parent, child, cIS.attach);
|
|
1630
1386
|
}
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1387
|
+
else if (is.three(parent, 'isObject3D') && is.three(child, 'isObject3D')) {
|
|
1388
|
+
parent.remove(child);
|
|
1389
|
+
const store = cIS?.store || pIS?.store;
|
|
1390
|
+
cIS?.removeInteraction?.(store);
|
|
1391
|
+
if (store)
|
|
1392
|
+
removeInteractivity(store, child);
|
|
1393
|
+
}
|
|
1394
|
+
// dispose
|
|
1395
|
+
const isPrimitive = cIS?.type && cIS.type !== 'ngt-primitive';
|
|
1396
|
+
if (!isPrimitive && child['dispose'] && !is.three(child, 'isScene')) {
|
|
1397
|
+
queueMicrotask(() => child['dispose']());
|
|
1398
|
+
}
|
|
1399
|
+
invalidateInstance(parent);
|
|
1637
1400
|
}
|
|
1638
|
-
function
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1401
|
+
function internalDestroyNode(node, removeChild) {
|
|
1402
|
+
const rS = node.__ngt_renderer__;
|
|
1403
|
+
if (!rS || rS[1 /* NgtRendererClassId.destroyed */])
|
|
1404
|
+
return;
|
|
1405
|
+
for (const child of rS[6 /* NgtRendererClassId.children */].slice()) {
|
|
1406
|
+
removeChild?.(node, child);
|
|
1407
|
+
internalDestroyNode(child, removeChild);
|
|
1408
|
+
}
|
|
1409
|
+
// clear out parent if haven't
|
|
1410
|
+
rS[5 /* NgtRendererClassId.parent */] = undefined;
|
|
1411
|
+
// clear out children
|
|
1412
|
+
rS[6 /* NgtRendererClassId.children */].length = 0;
|
|
1413
|
+
// clear out NgtInstanceState
|
|
1414
|
+
const iS = getInstanceState(node);
|
|
1415
|
+
if (iS) {
|
|
1416
|
+
const temp = iS;
|
|
1417
|
+
iS.removeInteraction?.(iS.store);
|
|
1418
|
+
delete temp['onAttach'];
|
|
1419
|
+
delete temp['onUpdate'];
|
|
1420
|
+
delete temp['object'];
|
|
1421
|
+
delete temp['objects'];
|
|
1422
|
+
delete temp['nonObjects'];
|
|
1423
|
+
delete temp['parent'];
|
|
1424
|
+
delete temp['add'];
|
|
1425
|
+
delete temp['remove'];
|
|
1426
|
+
delete temp['updateGeometryStamp'];
|
|
1427
|
+
delete temp['setParent'];
|
|
1428
|
+
delete temp['store'];
|
|
1429
|
+
delete temp['handlers'];
|
|
1430
|
+
delete temp['hierarchyStore'];
|
|
1431
|
+
delete temp['previousAttach'];
|
|
1432
|
+
delete temp['setPointerEvent'];
|
|
1433
|
+
delete temp['addInteraction'];
|
|
1434
|
+
delete temp['removeInteraction'];
|
|
1435
|
+
if (iS.type !== 'ngt-primitive') {
|
|
1436
|
+
delete node['__ngt__'];
|
|
1656
1437
|
}
|
|
1657
|
-
const options = inputOrOptions;
|
|
1658
|
-
const key = keyOrKeepUndefined;
|
|
1659
|
-
return computed(() => {
|
|
1660
|
-
const value = options()[key];
|
|
1661
|
-
if (keepUndefined && value == undefined)
|
|
1662
|
-
return undefined;
|
|
1663
|
-
if (typeof value === 'number')
|
|
1664
|
-
return new vectorCtor().setScalar(value);
|
|
1665
|
-
else if (Array.isArray(value))
|
|
1666
|
-
return new vectorCtor(...value);
|
|
1667
|
-
else if (value)
|
|
1668
|
-
return value;
|
|
1669
|
-
else
|
|
1670
|
-
return new vectorCtor();
|
|
1671
|
-
}, { equal: (a, b) => !!a && !!b && a.equals(b) });
|
|
1672
|
-
});
|
|
1673
|
-
}
|
|
1674
|
-
const vector2 = createVectorComputed(THREE.Vector2);
|
|
1675
|
-
const vector3 = createVectorComputed(THREE.Vector3);
|
|
1676
|
-
const vector4 = createVectorComputed(THREE.Vector4);
|
|
1677
|
-
|
|
1678
|
-
class NgtPortalAutoRender {
|
|
1679
|
-
constructor() {
|
|
1680
|
-
// TODO: (chau) investigate if this is still needed
|
|
1681
|
-
// effect(() => {
|
|
1682
|
-
// this.portalStore.update((state) => ({ events: { ...state.events, priority: this.renderPriority() + 1 } }));
|
|
1683
|
-
// });
|
|
1684
|
-
this.portalStore = injectStore({ host: true });
|
|
1685
|
-
this.parentStore = injectStore({ skipSelf: true });
|
|
1686
|
-
this.portal = inject(NgtPortalImpl, { host: true });
|
|
1687
|
-
this.renderPriority = input(1, { alias: 'autoRender', transform: (value) => numberAttribute(value, 1) });
|
|
1688
|
-
effect((onCleanup) => {
|
|
1689
|
-
const portalRendered = this.portal.portalRendered();
|
|
1690
|
-
if (!portalRendered)
|
|
1691
|
-
return;
|
|
1692
|
-
// track state
|
|
1693
|
-
const [renderPriority, { internal }] = [this.renderPriority(), this.portalStore()];
|
|
1694
|
-
let oldClean;
|
|
1695
|
-
const cleanup = internal.subscribe(({ gl, scene, camera }) => {
|
|
1696
|
-
const [parentScene, parentCamera] = [
|
|
1697
|
-
this.parentStore.snapshot.scene,
|
|
1698
|
-
this.parentStore.snapshot.camera,
|
|
1699
|
-
];
|
|
1700
|
-
oldClean = gl.autoClear;
|
|
1701
|
-
if (renderPriority === 1) {
|
|
1702
|
-
// clear scene and render with default
|
|
1703
|
-
gl.autoClear = true;
|
|
1704
|
-
gl.render(parentScene, parentCamera);
|
|
1705
|
-
}
|
|
1706
|
-
// disable cleaning
|
|
1707
|
-
gl.autoClear = false;
|
|
1708
|
-
gl.clearDepth();
|
|
1709
|
-
gl.render(scene, camera);
|
|
1710
|
-
// restore
|
|
1711
|
-
gl.autoClear = oldClean;
|
|
1712
|
-
}, renderPriority, this.portalStore);
|
|
1713
|
-
onCleanup(() => cleanup());
|
|
1714
|
-
});
|
|
1715
1438
|
}
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
static ngTemplateContextGuard(_, ctx) {
|
|
1725
|
-
return true;
|
|
1439
|
+
// clear our debugNode
|
|
1440
|
+
rS[4 /* NgtRendererClassId.injector */] = undefined;
|
|
1441
|
+
if (rS[0 /* NgtRendererClassId.type */] === 'comment') {
|
|
1442
|
+
delete node[NGT_INTERNAL_ADD_COMMENT_FLAG];
|
|
1443
|
+
delete node[NGT_INTERNAL_SET_PARENT_COMMENT_FLAG];
|
|
1444
|
+
delete node[NGT_CANVAS_CONTENT_FLAG];
|
|
1445
|
+
delete node[NGT_PORTAL_CONTENT_FLAG];
|
|
1446
|
+
delete node[NGT_DOM_PARENT_FLAG];
|
|
1726
1447
|
}
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
const store = injectStore();
|
|
1733
|
-
commentNode.data = NGT_PORTAL_CONTENT_FLAG;
|
|
1734
|
-
commentNode[NGT_PORTAL_CONTENT_FLAG] = store;
|
|
1735
|
-
commentNode[NGT_DOM_PARENT_FLAG] = host.nativeElement;
|
|
1448
|
+
// clear getAttribute if exist
|
|
1449
|
+
if ('getAttribute' in node &&
|
|
1450
|
+
typeof node['getAttribute'] === 'function' &&
|
|
1451
|
+
node['getAttribute'][NGT_GET_NODE_ATTRIBUTE_FLAG]) {
|
|
1452
|
+
delete node['getAttribute'];
|
|
1736
1453
|
}
|
|
1737
|
-
|
|
1738
|
-
|
|
1454
|
+
// mark node as destroyed
|
|
1455
|
+
rS[1 /* NgtRendererClassId.destroyed */] = true;
|
|
1739
1456
|
}
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
viewport = previousState.viewport.getCurrentViewport(camera, new THREE.Vector3(), size);
|
|
1753
|
-
// update the portal camera, if it differs from the previous layer
|
|
1754
|
-
if (camera !== previousState.camera)
|
|
1755
|
-
updateCamera(camera, size);
|
|
1457
|
+
|
|
1458
|
+
const NGT_RENDERER_OPTIONS = new InjectionToken('NGT_RENDERER_OPTIONS');
|
|
1459
|
+
class NgtRendererFactory2 {
|
|
1460
|
+
/**
|
|
1461
|
+
* NOTE: We use `useFactory` to instantiate `NgtRendererFactory2`
|
|
1462
|
+
*/
|
|
1463
|
+
constructor(delegateRendererFactory) {
|
|
1464
|
+
this.delegateRendererFactory = delegateRendererFactory;
|
|
1465
|
+
this.catalogue = injectCatalogue();
|
|
1466
|
+
this.document = inject(DOCUMENT);
|
|
1467
|
+
this.options = inject(NGT_RENDERER_OPTIONS, { optional: true }) || {};
|
|
1468
|
+
this.rendererMap = new Map();
|
|
1756
1469
|
}
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
size: { ...previousState.size, ...size },
|
|
1769
|
-
viewport: { ...previousState.viewport, ...viewport },
|
|
1770
|
-
// layers are allowed to override events
|
|
1771
|
-
setEvents: (events) => store.update((state) => ({ ...state, events: { ...state.events, ...events } })),
|
|
1772
|
-
};
|
|
1773
|
-
}
|
|
1774
|
-
class NgtPortalImpl {
|
|
1775
|
-
constructor() {
|
|
1776
|
-
this.container = input.required();
|
|
1777
|
-
this.state = input({});
|
|
1778
|
-
this.contentRef = contentChild.required(NgtPortalContent, { read: TemplateRef });
|
|
1779
|
-
this.anchorRef = contentChild.required(NgtPortalContent, { read: ViewContainerRef });
|
|
1780
|
-
this.previousStore = injectStore({ skipSelf: true });
|
|
1781
|
-
this.portalStore = injectStore();
|
|
1782
|
-
this.injector = inject(Injector);
|
|
1783
|
-
this.size = pick(this.state, 'size');
|
|
1784
|
-
this.events = pick(this.state, 'events');
|
|
1785
|
-
this.restState = omit(this.state, ['size', 'events']);
|
|
1786
|
-
this.portalContentRendered = signal(false);
|
|
1787
|
-
this.portalRendered = this.portalContentRendered.asReadonly();
|
|
1788
|
-
extend({ Group });
|
|
1789
|
-
effect(() => {
|
|
1790
|
-
let [container, anchor, content] = [
|
|
1791
|
-
this.container(),
|
|
1792
|
-
this.anchorRef(),
|
|
1793
|
-
this.contentRef(),
|
|
1794
|
-
this.previousStore(),
|
|
1795
|
-
];
|
|
1796
|
-
const [size, events, restState] = [untracked(this.size), untracked(this.events), untracked(this.restState)];
|
|
1797
|
-
if (!is.instance(container)) {
|
|
1798
|
-
container = prepare(container, 'ngt-portal', { store: this.portalStore });
|
|
1799
|
-
}
|
|
1800
|
-
const instanceState = getInstanceState(container);
|
|
1801
|
-
if (instanceState && instanceState.store !== this.portalStore) {
|
|
1802
|
-
instanceState.store = this.portalStore;
|
|
1470
|
+
createRenderer(hostElement, type) {
|
|
1471
|
+
const delegateRenderer = this.delegateRendererFactory.createRenderer(hostElement, type);
|
|
1472
|
+
if (!type)
|
|
1473
|
+
return delegateRenderer;
|
|
1474
|
+
let renderer = this.rendererMap.get(type.id);
|
|
1475
|
+
if (renderer) {
|
|
1476
|
+
if (renderer instanceof NgtRenderer2) {
|
|
1477
|
+
renderer.count += 1;
|
|
1478
|
+
if (renderer.delegateRenderer !== delegateRenderer) {
|
|
1479
|
+
renderer.delegateRenderer = delegateRenderer;
|
|
1480
|
+
}
|
|
1803
1481
|
}
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1482
|
+
return renderer;
|
|
1483
|
+
}
|
|
1484
|
+
if (hostElement && !isRendererNode(hostElement)) {
|
|
1485
|
+
createRendererNode('platform', hostElement, this.document);
|
|
1486
|
+
}
|
|
1487
|
+
if (Reflect.get(type, 'type')?.[NGT_HTML_FLAG]) {
|
|
1488
|
+
this.rendererMap.set(type.id, delegateRenderer);
|
|
1489
|
+
// patch delegate destroyNode so we can destroy this HTML node
|
|
1490
|
+
// TODO: make sure we really need to do this
|
|
1491
|
+
const originalDestroyNode = delegateRenderer.destroyNode?.bind(delegateRenderer);
|
|
1492
|
+
if (!originalDestroyNode || !(NGT_DELEGATE_RENDERER_DESTROY_NODE_PATCHED_FLAG in originalDestroyNode)) {
|
|
1493
|
+
delegateRenderer.destroyNode = (node) => {
|
|
1494
|
+
originalDestroyNode?.(node);
|
|
1495
|
+
if (node !== hostElement)
|
|
1496
|
+
return;
|
|
1497
|
+
internalDestroyNode(node, null);
|
|
1498
|
+
};
|
|
1499
|
+
Object.assign(delegateRenderer.destroyNode, {
|
|
1500
|
+
[NGT_DELEGATE_RENDERER_DESTROY_NODE_PATCHED_FLAG]: true,
|
|
1501
|
+
});
|
|
1808
1502
|
}
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1503
|
+
return delegateRenderer;
|
|
1504
|
+
}
|
|
1505
|
+
this.rendererMap.set(type.id, (renderer = new NgtRenderer2(delegateRenderer, this.catalogue, this.document, this.options)));
|
|
1506
|
+
return renderer;
|
|
1813
1507
|
}
|
|
1814
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type:
|
|
1815
|
-
static { this.ɵ
|
|
1816
|
-
{
|
|
1817
|
-
provide: NGT_STORE,
|
|
1818
|
-
useFactory: (previousStore) => {
|
|
1819
|
-
const pointer = new THREE.Vector2();
|
|
1820
|
-
const raycaster = new THREE.Raycaster();
|
|
1821
|
-
const { id: _skipId, ...previousState } = previousStore.snapshot;
|
|
1822
|
-
const store = signalState({
|
|
1823
|
-
id: makeId(),
|
|
1824
|
-
...previousState,
|
|
1825
|
-
scene: null,
|
|
1826
|
-
previousRoot: previousStore,
|
|
1827
|
-
pointer,
|
|
1828
|
-
raycaster,
|
|
1829
|
-
});
|
|
1830
|
-
store.update(mergeState(previousStore, store, null, pointer, raycaster));
|
|
1831
|
-
return store;
|
|
1832
|
-
},
|
|
1833
|
-
deps: [[new SkipSelf(), NGT_STORE]],
|
|
1834
|
-
},
|
|
1835
|
-
], queries: [{ propertyName: "contentRef", first: true, predicate: NgtPortalContent, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "anchorRef", first: true, predicate: NgtPortalContent, descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, template: `
|
|
1836
|
-
@if (portalRendered()) {
|
|
1837
|
-
<!-- Without an element that receives pointer events state.pointer will always be 0/0 -->
|
|
1838
|
-
<ngt-group (pointerover)="(undefined)" attach="none" />
|
|
1839
|
-
}
|
|
1840
|
-
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1508
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgtRendererFactory2, deps: [{ token: i0.RendererFactory2 }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1509
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgtRendererFactory2 }); }
|
|
1841
1510
|
}
|
|
1842
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type:
|
|
1843
|
-
type:
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
pointer,
|
|
1867
|
-
raycaster,
|
|
1868
|
-
});
|
|
1869
|
-
store.update(mergeState(previousStore, store, null, pointer, raycaster));
|
|
1870
|
-
return store;
|
|
1871
|
-
},
|
|
1872
|
-
deps: [[new SkipSelf(), NGT_STORE]],
|
|
1873
|
-
},
|
|
1874
|
-
],
|
|
1875
|
-
}]
|
|
1876
|
-
}], ctorParameters: () => [] });
|
|
1877
|
-
const NgtPortal = [NgtPortalImpl, NgtPortalContent];
|
|
1878
|
-
|
|
1879
|
-
// This function prepares a set of changes to be applied to the instance
|
|
1880
|
-
function diffProps(instance, props) {
|
|
1881
|
-
const changes = [];
|
|
1882
|
-
for (const propKey in props) {
|
|
1883
|
-
const propValue = props[propKey];
|
|
1884
|
-
let key = propKey;
|
|
1885
|
-
if (is.colorSpaceExist(instance)) {
|
|
1886
|
-
if (propKey === 'encoding') {
|
|
1887
|
-
key = 'colorSpace';
|
|
1888
|
-
}
|
|
1889
|
-
else if (propKey === 'outputEncoding') {
|
|
1890
|
-
key = 'outputColorSpace';
|
|
1891
|
-
}
|
|
1511
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgtRendererFactory2, decorators: [{
|
|
1512
|
+
type: Injectable
|
|
1513
|
+
}], ctorParameters: () => [{ type: i0.RendererFactory2 }] });
|
|
1514
|
+
class NgtRenderer2 {
|
|
1515
|
+
constructor(delegateRenderer, catalogue, document, options, count = 1) {
|
|
1516
|
+
this.delegateRenderer = delegateRenderer;
|
|
1517
|
+
this.catalogue = catalogue;
|
|
1518
|
+
this.document = document;
|
|
1519
|
+
this.options = options;
|
|
1520
|
+
this.count = count;
|
|
1521
|
+
this.argsInjectors = [];
|
|
1522
|
+
this.parentInjectors = [];
|
|
1523
|
+
this.destroyNode = (node) => {
|
|
1524
|
+
internalDestroyNode(node, this.removeChild.bind(this));
|
|
1525
|
+
};
|
|
1526
|
+
this.addClass = this.delegateRenderer.addClass.bind(this.delegateRenderer);
|
|
1527
|
+
this.removeClass = this.delegateRenderer.removeClass.bind(this.delegateRenderer);
|
|
1528
|
+
this.setStyle = this.delegateRenderer.setStyle.bind(this.delegateRenderer);
|
|
1529
|
+
this.removeStyle = this.delegateRenderer.removeStyle.bind(this.delegateRenderer);
|
|
1530
|
+
this.selectRootElement = this.delegateRenderer.selectRootElement.bind(this.delegateRenderer);
|
|
1531
|
+
this.nextSibling = this.delegateRenderer.nextSibling.bind(this.delegateRenderer);
|
|
1532
|
+
this.setValue = this.delegateRenderer.setValue.bind(this.delegateRenderer);
|
|
1533
|
+
if (!this.options.verbose) {
|
|
1534
|
+
this.options.verbose = false;
|
|
1892
1535
|
}
|
|
1893
|
-
if (is.equ(propValue, instance[key]))
|
|
1894
|
-
continue;
|
|
1895
|
-
changes.push([propKey, propValue]);
|
|
1896
1536
|
}
|
|
1897
|
-
|
|
1898
|
-
}
|
|
1899
|
-
// NOTE: this is a workaround to give the instance a chance to have the store from the parent.
|
|
1900
|
-
// we clear this property after the applyProps is done
|
|
1901
|
-
const NGT_APPLY_PROPS = '__ngt_apply_props__';
|
|
1902
|
-
// https://github.com/mrdoob/three.js/pull/27042
|
|
1903
|
-
// https://github.com/mrdoob/three.js/pull/22748
|
|
1904
|
-
const colorMaps = ['map', 'emissiveMap', 'sheenColorMap', 'specularColorMap', 'envMap'];
|
|
1905
|
-
function resolveInstanceKey(instance, key) {
|
|
1906
|
-
let targetProp = instance[key];
|
|
1907
|
-
if (!key.includes('.'))
|
|
1908
|
-
return { root: instance, targetKey: key, targetProp };
|
|
1909
|
-
// Resolve pierced target
|
|
1910
|
-
targetProp = instance;
|
|
1911
|
-
for (const part of key.split('.')) {
|
|
1912
|
-
key = part;
|
|
1913
|
-
instance = targetProp;
|
|
1914
|
-
targetProp = targetProp[key];
|
|
1537
|
+
get data() {
|
|
1538
|
+
return { ...this.delegateRenderer.data, __ngt_renderer__: true };
|
|
1915
1539
|
}
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
// // Switch root if atomic
|
|
1921
|
-
// if (!targetProp?.set) instance = chain.reduce((acc, part) => acc[part], instance);
|
|
1922
|
-
return { root: instance, targetKey: key, targetProp };
|
|
1923
|
-
}
|
|
1924
|
-
// This function applies a set of changes to the instance
|
|
1925
|
-
function applyProps(instance, props) {
|
|
1926
|
-
// if props is empty
|
|
1927
|
-
if (!Object.keys(props).length)
|
|
1928
|
-
return instance;
|
|
1929
|
-
const localState = getInstanceState(instance);
|
|
1930
|
-
const rootState = localState?.store?.snapshot ?? instance[NGT_APPLY_PROPS]?.snapshot ?? {};
|
|
1931
|
-
const changes = diffProps(instance, props);
|
|
1932
|
-
for (let i = 0; i < changes.length; i++) {
|
|
1933
|
-
let [key, value] = changes[i];
|
|
1934
|
-
// Ignore setting undefined props
|
|
1935
|
-
// https://github.com/pmndrs/react-three-fiber/issues/274
|
|
1936
|
-
if (value === undefined)
|
|
1937
|
-
continue;
|
|
1938
|
-
// Alias (output)encoding => (output)colorSpace (since r152)
|
|
1939
|
-
// https://github.com/pmndrs/react-three-fiber/pull/2829
|
|
1940
|
-
// if (is.colorSpaceExist(instance)) {
|
|
1941
|
-
// const sRGBEncoding = 3001;
|
|
1942
|
-
// const SRGBColorSpace = 'srgb';
|
|
1943
|
-
// const LinearSRGBColorSpace = 'srgb-linear';
|
|
1944
|
-
//
|
|
1945
|
-
// if (key === 'encoding') {
|
|
1946
|
-
// key = 'colorSpace';
|
|
1947
|
-
// value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
|
|
1948
|
-
// } else if (key === 'outputEncoding') {
|
|
1949
|
-
// key = 'outputColorSpace';
|
|
1950
|
-
// value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
|
|
1951
|
-
// }
|
|
1952
|
-
// }
|
|
1953
|
-
const { root, targetKey, targetProp } = resolveInstanceKey(instance, key);
|
|
1954
|
-
// we have switched due to pierced props
|
|
1955
|
-
if (root !== instance) {
|
|
1956
|
-
return applyProps(root, { [targetKey]: value });
|
|
1540
|
+
destroy() {
|
|
1541
|
+
if (this.count > 1) {
|
|
1542
|
+
this.count -= 1;
|
|
1543
|
+
return;
|
|
1957
1544
|
}
|
|
1958
|
-
//
|
|
1959
|
-
|
|
1960
|
-
|
|
1545
|
+
// this is the last instance of the same NgtRenderer2
|
|
1546
|
+
this.count = 0;
|
|
1547
|
+
this.argsInjectors = [];
|
|
1548
|
+
this.parentInjectors = [];
|
|
1549
|
+
}
|
|
1550
|
+
createElement(name, namespace) {
|
|
1551
|
+
const platformElement = this.delegateRenderer.createElement(name, namespace);
|
|
1552
|
+
if (name === 'ngt-portal') {
|
|
1553
|
+
return createRendererNode('portal', platformElement, this.document);
|
|
1961
1554
|
}
|
|
1962
|
-
|
|
1963
|
-
|
|
1555
|
+
if (name === 'ngt-value') {
|
|
1556
|
+
return createRendererNode('three', prepare(platformElement, 'ngt-value'), this.document);
|
|
1964
1557
|
}
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1558
|
+
const [injectedArgs, injectedParent] = [
|
|
1559
|
+
this.getNgtDirective(NgtArgs, this.argsInjectors)?.value || [],
|
|
1560
|
+
this.getNgtDirective(NgtParent, this.parentInjectors)?.value,
|
|
1561
|
+
];
|
|
1562
|
+
if (name === 'ngt-primitive') {
|
|
1563
|
+
if (!injectedArgs[0])
|
|
1564
|
+
throw new Error(`[NGT] ngt-primitive without args is invalid`);
|
|
1565
|
+
const object = injectedArgs[0];
|
|
1566
|
+
let instanceState = getInstanceState(object);
|
|
1567
|
+
if (!instanceState || instanceState.type !== 'ngt-primitive') {
|
|
1568
|
+
// if an object isn't already "prepared", we'll prepare it
|
|
1569
|
+
prepare(object, 'ngt-primitive', instanceState);
|
|
1975
1570
|
}
|
|
1976
|
-
|
|
1977
|
-
|
|
1571
|
+
const primitiveRendererNode = createRendererNode('three', object, this.document);
|
|
1572
|
+
if (injectedParent) {
|
|
1573
|
+
primitiveRendererNode.__ngt_renderer__[5 /* NgtRendererClassId.parent */] =
|
|
1574
|
+
injectedParent;
|
|
1978
1575
|
}
|
|
1576
|
+
return primitiveRendererNode;
|
|
1979
1577
|
}
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
if (typeof targetProp.fromArray === 'function')
|
|
1983
|
-
targetProp.fromArray(value);
|
|
1984
|
-
else
|
|
1985
|
-
targetProp.set(...value);
|
|
1986
|
-
}
|
|
1987
|
-
// Set literal types
|
|
1988
|
-
else if (targetProp && typeof targetProp.set === 'function' && typeof value !== 'object') {
|
|
1989
|
-
const isColor = is.three(targetProp, 'isColor');
|
|
1990
|
-
// Allow setting array scalars
|
|
1991
|
-
if (!isColor && typeof targetProp.setScalar === 'function' && typeof value === 'number')
|
|
1992
|
-
targetProp.setScalar(value);
|
|
1993
|
-
// Otherwise just set single value
|
|
1994
|
-
else
|
|
1995
|
-
targetProp.set(value);
|
|
1578
|
+
if (!name.startsWith('ngt-')) {
|
|
1579
|
+
return createRendererNode('platform', platformElement, this.document);
|
|
1996
1580
|
}
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
!rootState.linear &&
|
|
2005
|
-
colorMaps.includes(targetKey) &&
|
|
2006
|
-
root[targetKey]?.isTexture &&
|
|
2007
|
-
// sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
|
|
2008
|
-
root[targetKey].format === THREE.RGBAFormat &&
|
|
2009
|
-
root[targetKey].type === THREE.UnsignedByteType) {
|
|
2010
|
-
// NOTE: this cannot be set from the renderer (e.g. sRGB source textures rendered to P3)
|
|
2011
|
-
root[targetKey].colorSpace = THREE.SRGBColorSpace;
|
|
1581
|
+
const threeName = kebabToPascal(name.startsWith('ngt-') ? name.slice(4) : name);
|
|
1582
|
+
let threeTarget = this.catalogue[threeName];
|
|
1583
|
+
if (!threeTarget && threeName in THREE) {
|
|
1584
|
+
const threeSymbol = THREE[threeName];
|
|
1585
|
+
if (typeof threeSymbol === 'function') {
|
|
1586
|
+
// we will attempt to prefill the catalogue with symbols from THREE
|
|
1587
|
+
threeTarget = this.catalogue[threeName] = threeSymbol;
|
|
2012
1588
|
}
|
|
2013
1589
|
}
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
if (instance[NGT_APPLY_PROPS])
|
|
2034
|
-
delete instance[NGT_APPLY_PROPS];
|
|
2035
|
-
return instance;
|
|
2036
|
-
}
|
|
2037
|
-
|
|
2038
|
-
function isRendererNode(node) {
|
|
2039
|
-
return !!node && typeof node === 'object' && NGT_RENDERER_NODE_FLAG in node;
|
|
2040
|
-
}
|
|
2041
|
-
function createRendererNode(type, node, document) {
|
|
2042
|
-
const state = [type, false, undefined, undefined, undefined, undefined, []];
|
|
2043
|
-
const rendererNode = Object.assign(node, { [NGT_RENDERER_NODE_FLAG]: state });
|
|
2044
|
-
// NOTE: assign ownerDocument to node so we can use HostListener in Component
|
|
2045
|
-
if (!rendererNode['ownerDocument'])
|
|
2046
|
-
rendererNode['ownerDocument'] = document;
|
|
2047
|
-
// NOTE: Angular SSR calls `node.getAttribute()` to retrieve hydration info on a node
|
|
2048
|
-
if (!('getAttribute' in rendererNode) || typeof rendererNode['getAttribute'] !== 'function') {
|
|
2049
|
-
const getNodeAttribute = (name) => rendererNode[name];
|
|
2050
|
-
getNodeAttribute[NGT_GET_NODE_ATTRIBUTE_FLAG] = true;
|
|
2051
|
-
Object.defineProperty(rendererNode, 'getAttribute', { value: getNodeAttribute, configurable: true });
|
|
2052
|
-
}
|
|
2053
|
-
return rendererNode;
|
|
2054
|
-
}
|
|
2055
|
-
function setRendererParentNode(node, parent) {
|
|
2056
|
-
if (!node.__ngt_renderer__[5 /* NgtRendererClassId.parent */]) {
|
|
2057
|
-
node.__ngt_renderer__[5 /* NgtRendererClassId.parent */] = parent;
|
|
2058
|
-
}
|
|
2059
|
-
}
|
|
2060
|
-
function addRendererChildNode(node, child) {
|
|
2061
|
-
if (!node.__ngt_renderer__[6 /* NgtRendererClassId.children */].includes(child)) {
|
|
2062
|
-
node.__ngt_renderer__[6 /* NgtRendererClassId.children */].push(child);
|
|
2063
|
-
}
|
|
2064
|
-
}
|
|
2065
|
-
|
|
2066
|
-
function attach(object, value, paths = [], useApplyProps = false) {
|
|
2067
|
-
const [base, ...remaining] = paths;
|
|
2068
|
-
if (!base)
|
|
2069
|
-
return;
|
|
2070
|
-
if (remaining.length === 0) {
|
|
2071
|
-
if (useApplyProps)
|
|
2072
|
-
applyProps(object, { [base]: value });
|
|
2073
|
-
else
|
|
2074
|
-
object[base] = value;
|
|
2075
|
-
}
|
|
2076
|
-
else {
|
|
2077
|
-
assignEmpty(object, base, useApplyProps);
|
|
2078
|
-
attach(object[base], value, remaining, useApplyProps);
|
|
1590
|
+
if (threeTarget) {
|
|
1591
|
+
const threeInstance = prepare(new threeTarget(...injectedArgs), name);
|
|
1592
|
+
const rendererNode = createRendererNode('three', threeInstance, this.document);
|
|
1593
|
+
// assert type here because it is just created so we don't have to null check it
|
|
1594
|
+
const instanceState = getInstanceState(threeInstance);
|
|
1595
|
+
// auto-attach for geometry and material
|
|
1596
|
+
if (is.three(threeInstance, 'isBufferGeometry')) {
|
|
1597
|
+
instanceState.attach = ['geometry'];
|
|
1598
|
+
}
|
|
1599
|
+
else if (is.three(threeInstance, 'isMaterial')) {
|
|
1600
|
+
instanceState.attach = ['material'];
|
|
1601
|
+
}
|
|
1602
|
+
if (injectedParent) {
|
|
1603
|
+
rendererNode.__ngt_renderer__[5 /* NgtRendererClassId.parent */] =
|
|
1604
|
+
injectedParent;
|
|
1605
|
+
}
|
|
1606
|
+
return rendererNode;
|
|
1607
|
+
}
|
|
1608
|
+
return createRendererNode('platform', platformElement, this.document);
|
|
2079
1609
|
}
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
1610
|
+
createComment(value) {
|
|
1611
|
+
const commentNode = this.delegateRenderer.createComment(value);
|
|
1612
|
+
const commentRendererNode = createRendererNode('comment', commentNode, this.document);
|
|
1613
|
+
// NOTE: we attach an arrow function to the Comment node
|
|
1614
|
+
// In our directives, we can call this function to then start tracking the RendererNode
|
|
1615
|
+
// this is done to limit the amount of Nodes we need to process for getCreationState
|
|
1616
|
+
Object.assign(commentRendererNode, {
|
|
1617
|
+
[NGT_INTERNAL_ADD_COMMENT_FLAG]: (type, injector) => {
|
|
1618
|
+
if (type === 'args') {
|
|
1619
|
+
this.argsInjectors.push(injector);
|
|
1620
|
+
}
|
|
1621
|
+
else if (type === 'parent') {
|
|
1622
|
+
Object.assign(commentRendererNode, {
|
|
1623
|
+
[NGT_INTERNAL_SET_PARENT_COMMENT_FLAG]: (ngtParent) => {
|
|
1624
|
+
commentRendererNode.__ngt_renderer__[5 /* NgtRendererClassId.parent */] = ngtParent;
|
|
1625
|
+
},
|
|
1626
|
+
});
|
|
1627
|
+
this.parentInjectors.push(injector);
|
|
1628
|
+
}
|
|
1629
|
+
commentRendererNode.__ngt_renderer__[4 /* NgtRendererClassId.injector */] = injector;
|
|
1630
|
+
},
|
|
1631
|
+
});
|
|
1632
|
+
return commentRendererNode;
|
|
2088
1633
|
}
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
obj[base] = {};
|
|
1634
|
+
createText(value) {
|
|
1635
|
+
const textNode = this.delegateRenderer.createText(value);
|
|
1636
|
+
return createRendererNode('text', textNode, this.document);
|
|
2093
1637
|
}
|
|
2094
|
-
|
|
2095
|
-
const
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
const
|
|
2100
|
-
|
|
2101
|
-
|
|
1638
|
+
appendChild(parent, newChild, refChild, isMove) {
|
|
1639
|
+
const delegatedFn = refChild
|
|
1640
|
+
? this.delegateRenderer.insertBefore.bind(this.delegateRenderer, parent, newChild, refChild, isMove)
|
|
1641
|
+
: this.delegateRenderer.appendChild.bind(this.delegateRenderer, parent, newChild);
|
|
1642
|
+
const pRS = parent.__ngt_renderer__;
|
|
1643
|
+
const cRS = newChild.__ngt_renderer__;
|
|
1644
|
+
if (!pRS || !cRS) {
|
|
1645
|
+
this.options.verbose &&
|
|
1646
|
+
console.warn('[NGT dev mode] One of parent or child is not a renderer node.', { parent, newChild });
|
|
1647
|
+
return delegatedFn();
|
|
1648
|
+
}
|
|
1649
|
+
if (cRS[0 /* NgtRendererClassId.type */] === 'comment') {
|
|
1650
|
+
// if child is a comment, we'll set the parent then bail.
|
|
1651
|
+
// comment usually means it's part of a templateRef ViewContainerRef or structural directive
|
|
1652
|
+
setRendererParentNode(newChild, parent);
|
|
1653
|
+
// if parent is not three, we'll delegate to the renderer
|
|
1654
|
+
if (pRS[0 /* NgtRendererClassId.type */] !== 'three') {
|
|
1655
|
+
delegatedFn();
|
|
1656
|
+
}
|
|
2102
1657
|
return;
|
|
2103
|
-
Object.assign(obj[base], { [NGT_APPLY_PROPS]: parentInstanceState.store });
|
|
2104
|
-
}
|
|
2105
|
-
}
|
|
2106
|
-
function createAttachFunction(cb) {
|
|
2107
|
-
return (parent, child, store) => cb({ parent, child, store });
|
|
2108
|
-
}
|
|
2109
|
-
|
|
2110
|
-
function kebabToPascal(str) {
|
|
2111
|
-
if (!str)
|
|
2112
|
-
return str; // Handle empty input
|
|
2113
|
-
let pascalStr = '';
|
|
2114
|
-
let capitalizeNext = true; // Flag to track capitalization
|
|
2115
|
-
for (let i = 0; i < str.length; i++) {
|
|
2116
|
-
const char = str[i];
|
|
2117
|
-
if (char === '-') {
|
|
2118
|
-
capitalizeNext = true;
|
|
2119
|
-
continue;
|
|
2120
1658
|
}
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
if (!iS || !pIS)
|
|
2130
|
-
return;
|
|
2131
|
-
// assign store on child if not already exist
|
|
2132
|
-
// or child store is not the same as parent store
|
|
2133
|
-
// or child store is the parent of parent store
|
|
2134
|
-
if (!iS.store || iS.store !== pIS.store || iS.store === pIS.store.snapshot.previousRoot) {
|
|
2135
|
-
iS.store = pIS.store;
|
|
2136
|
-
// Call addInteraction if it exists
|
|
2137
|
-
iS.addInteraction?.(pIS.store);
|
|
2138
|
-
// Collect all children (objects and nonObjects)
|
|
2139
|
-
const children = [
|
|
2140
|
-
...(iS.objects ? untracked(iS.objects) : []),
|
|
2141
|
-
...(iS.nonObjects ? untracked(iS.nonObjects) : []),
|
|
2142
|
-
];
|
|
2143
|
-
// Recursively reassign the store for each child
|
|
2144
|
-
for (const child of children) {
|
|
2145
|
-
propagateStoreRecursively(child, node);
|
|
1659
|
+
if (pRS[0 /* NgtRendererClassId.type */] === 'platform' && cRS[0 /* NgtRendererClassId.type */] === 'platform') {
|
|
1660
|
+
if (newChild[NGT_DOM_PARENT_FLAG] && newChild[NGT_DOM_PARENT_FLAG] instanceof HTMLElement) {
|
|
1661
|
+
return this.delegateRenderer.appendChild(newChild[NGT_DOM_PARENT_FLAG], newChild);
|
|
1662
|
+
}
|
|
1663
|
+
if (pRS[5 /* NgtRendererClassId.parent */] && !cRS[5 /* NgtRendererClassId.parent */]) {
|
|
1664
|
+
return this.appendChild(pRS[5 /* NgtRendererClassId.parent */], newChild);
|
|
1665
|
+
}
|
|
1666
|
+
return delegatedFn();
|
|
2146
1667
|
}
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
let added = false;
|
|
2157
|
-
// propagate store recursively
|
|
2158
|
-
propagateStoreRecursively(child, parent);
|
|
2159
|
-
if (cIS.attach) {
|
|
2160
|
-
const attachProp = cIS.attach;
|
|
2161
|
-
if (typeof attachProp === 'function') {
|
|
2162
|
-
let attachCleanUp = undefined;
|
|
2163
|
-
if (cIS.type === 'ngt-value') {
|
|
2164
|
-
if (cIS.hierarchyStore.snapshot.parent !== parent) {
|
|
2165
|
-
cIS.setParent(parent);
|
|
2166
|
-
}
|
|
2167
|
-
// at this point we don't have rawValue yet, so we bail and wait until the Renderer recalls attach
|
|
2168
|
-
if (child.__ngt_renderer__[2 /* NgtRendererClassId.rawValue */] === undefined)
|
|
2169
|
-
return;
|
|
2170
|
-
attachCleanUp = attachProp(parent, child.__ngt_renderer__[2 /* NgtRendererClassId.rawValue */], cIS.store);
|
|
1668
|
+
if (pRS[0 /* NgtRendererClassId.type */] === 'three' && cRS[0 /* NgtRendererClassId.type */] === 'three') {
|
|
1669
|
+
return this.appendThreeRendererNodes(parent, newChild);
|
|
1670
|
+
}
|
|
1671
|
+
if (pRS[0 /* NgtRendererClassId.type */] === 'platform' && cRS[0 /* NgtRendererClassId.type */] === 'three') {
|
|
1672
|
+
// if platform has parent, delegate to that parent
|
|
1673
|
+
if (pRS[5 /* NgtRendererClassId.parent */]) {
|
|
1674
|
+
// but track the child for this parent as well
|
|
1675
|
+
addRendererChildNode(parent, newChild);
|
|
1676
|
+
return this.appendChild(pRS[5 /* NgtRendererClassId.parent */], newChild);
|
|
2171
1677
|
}
|
|
2172
|
-
|
|
2173
|
-
|
|
1678
|
+
// platform can also have normal parentNode
|
|
1679
|
+
const platformParentNode = this.delegateRenderer.parentNode(parent);
|
|
1680
|
+
if (platformParentNode) {
|
|
1681
|
+
return this.appendChild(platformParentNode, newChild);
|
|
2174
1682
|
}
|
|
2175
|
-
if
|
|
2176
|
-
|
|
1683
|
+
// if not, set up parent and child relationship for this pair then bail
|
|
1684
|
+
this.setNodeRelationship(parent, newChild);
|
|
1685
|
+
return;
|
|
2177
1686
|
}
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
invalidateInstance(child);
|
|
2182
|
-
return;
|
|
1687
|
+
if (pRS[0 /* NgtRendererClassId.type */] === 'three' && cRS[0 /* NgtRendererClassId.type */] === 'platform') {
|
|
1688
|
+
if (!cRS[5 /* NgtRendererClassId.parent */]) {
|
|
1689
|
+
setRendererParentNode(newChild, parent);
|
|
2183
1690
|
}
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
attachProp[1] &&
|
|
2187
|
-
typeof Number(attachProp[1]) === 'number' &&
|
|
2188
|
-
is.three(child, 'isMaterial') &&
|
|
2189
|
-
!Array.isArray(parent['material'])) {
|
|
2190
|
-
parent['material'] = [];
|
|
1691
|
+
for (const child of cRS[6 /* NgtRendererClassId.children */]) {
|
|
1692
|
+
this.appendChild(parent, child);
|
|
2191
1693
|
}
|
|
2192
|
-
|
|
2193
|
-
if (
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
if (child.__ngt_renderer__[2 /* NgtRendererClassId.rawValue */] === undefined)
|
|
2198
|
-
return;
|
|
2199
|
-
// save prev value
|
|
2200
|
-
cIS.previousAttach = attachProp.reduce((value, key) => value[key], parent);
|
|
2201
|
-
attach(parent, child.__ngt_renderer__[2 /* NgtRendererClassId.rawValue */], attachProp, true);
|
|
2202
|
-
}
|
|
2203
|
-
else {
|
|
2204
|
-
// save prev value
|
|
2205
|
-
cIS.previousAttach = attachProp.reduce((value, key) => value[key], parent);
|
|
2206
|
-
attach(parent, child, attachProp);
|
|
1694
|
+
for (const platformChildNode of newChild['childNodes'] || []) {
|
|
1695
|
+
if (!isRendererNode(platformChildNode) ||
|
|
1696
|
+
platformChildNode.__ngt_renderer__[0 /* NgtRendererClassId.type */] !== 'platform')
|
|
1697
|
+
continue;
|
|
1698
|
+
this.appendChild(parent, platformChildNode);
|
|
2207
1699
|
}
|
|
1700
|
+
return;
|
|
2208
1701
|
}
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
added = true;
|
|
2213
|
-
cIS.addInteraction?.(cIS.store || pIS.store);
|
|
2214
|
-
}
|
|
2215
|
-
if (pIS.add) {
|
|
2216
|
-
pIS.add(child, added ? 'objects' : 'nonObjects');
|
|
2217
|
-
}
|
|
2218
|
-
if (cIS.parent && untracked(cIS.parent) !== parent) {
|
|
2219
|
-
cIS.setParent(parent);
|
|
2220
|
-
}
|
|
2221
|
-
// NOTE: this does not mean that the child is actually attached to the parent on the scenegraph.
|
|
2222
|
-
// a child on the Angular template can also emit onAttach
|
|
2223
|
-
if (cIS.onAttach)
|
|
2224
|
-
cIS.onAttach({ parent, node: child });
|
|
2225
|
-
invalidateInstance(child);
|
|
2226
|
-
invalidateInstance(parent);
|
|
2227
|
-
}
|
|
2228
|
-
function removeThreeChild(child, parent, dispose) {
|
|
2229
|
-
const pIS = getInstanceState(parent);
|
|
2230
|
-
const cIS = getInstanceState(child);
|
|
2231
|
-
// clear parent ref
|
|
2232
|
-
cIS?.setParent(null);
|
|
2233
|
-
// remove child from parent
|
|
2234
|
-
pIS?.remove?.(child, 'objects');
|
|
2235
|
-
pIS?.remove?.(child, 'nonObjects');
|
|
2236
|
-
if (cIS?.attach) {
|
|
2237
|
-
detach(parent, child, cIS.attach);
|
|
2238
|
-
}
|
|
2239
|
-
else if (is.three(parent, 'isObject3D') && is.three(child, 'isObject3D')) {
|
|
2240
|
-
parent.remove(child);
|
|
2241
|
-
const store = cIS?.store || pIS?.store;
|
|
2242
|
-
cIS?.removeInteraction?.(store);
|
|
2243
|
-
if (store)
|
|
2244
|
-
removeInteractivity(store, child);
|
|
2245
|
-
}
|
|
2246
|
-
// dispose
|
|
2247
|
-
const isPrimitive = cIS?.type && cIS.type !== 'ngt-primitive';
|
|
2248
|
-
if (!isPrimitive && child['dispose'] && !is.three(child, 'isScene')) {
|
|
2249
|
-
queueMicrotask(() => child['dispose']());
|
|
2250
|
-
}
|
|
2251
|
-
invalidateInstance(parent);
|
|
2252
|
-
}
|
|
2253
|
-
function internalDestroyNode(node, removeChild) {
|
|
2254
|
-
const rS = node.__ngt_renderer__;
|
|
2255
|
-
if (!rS || rS[1 /* NgtRendererClassId.destroyed */])
|
|
2256
|
-
return;
|
|
2257
|
-
for (const child of rS[6 /* NgtRendererClassId.children */].slice()) {
|
|
2258
|
-
removeChild?.(node, child);
|
|
2259
|
-
internalDestroyNode(child, removeChild);
|
|
2260
|
-
}
|
|
2261
|
-
// clear out parent if haven't
|
|
2262
|
-
rS[5 /* NgtRendererClassId.parent */] = undefined;
|
|
2263
|
-
// clear out children
|
|
2264
|
-
rS[6 /* NgtRendererClassId.children */].length = 0;
|
|
2265
|
-
// clear out NgtInstanceState
|
|
2266
|
-
const iS = getInstanceState(node);
|
|
2267
|
-
if (iS) {
|
|
2268
|
-
const temp = iS;
|
|
2269
|
-
iS.removeInteraction?.(iS.store);
|
|
2270
|
-
delete temp['onAttach'];
|
|
2271
|
-
delete temp['onUpdate'];
|
|
2272
|
-
delete temp['object'];
|
|
2273
|
-
delete temp['objects'];
|
|
2274
|
-
delete temp['nonObjects'];
|
|
2275
|
-
delete temp['parent'];
|
|
2276
|
-
delete temp['add'];
|
|
2277
|
-
delete temp['remove'];
|
|
2278
|
-
delete temp['updateGeometryStamp'];
|
|
2279
|
-
delete temp['setParent'];
|
|
2280
|
-
delete temp['store'];
|
|
2281
|
-
delete temp['handlers'];
|
|
2282
|
-
delete temp['hierarchyStore'];
|
|
2283
|
-
delete temp['previousAttach'];
|
|
2284
|
-
delete temp['setPointerEvent'];
|
|
2285
|
-
delete temp['addInteraction'];
|
|
2286
|
-
delete temp['removeInteraction'];
|
|
2287
|
-
if (iS.type !== 'ngt-primitive') {
|
|
2288
|
-
delete node['__ngt__'];
|
|
2289
|
-
}
|
|
2290
|
-
}
|
|
2291
|
-
// clear our debugNode
|
|
2292
|
-
rS[4 /* NgtRendererClassId.injector */] = undefined;
|
|
2293
|
-
if (rS[0 /* NgtRendererClassId.type */] === 'comment') {
|
|
2294
|
-
delete node[NGT_INTERNAL_ADD_COMMENT_FLAG];
|
|
2295
|
-
delete node[NGT_INTERNAL_SET_PARENT_COMMENT_FLAG];
|
|
2296
|
-
delete node[NGT_CANVAS_CONTENT_FLAG];
|
|
2297
|
-
delete node[NGT_PORTAL_CONTENT_FLAG];
|
|
2298
|
-
delete node[NGT_DOM_PARENT_FLAG];
|
|
2299
|
-
}
|
|
2300
|
-
// clear getAttribute if exist
|
|
2301
|
-
if ('getAttribute' in node &&
|
|
2302
|
-
typeof node['getAttribute'] === 'function' &&
|
|
2303
|
-
node['getAttribute'][NGT_GET_NODE_ATTRIBUTE_FLAG]) {
|
|
2304
|
-
delete node['getAttribute'];
|
|
2305
|
-
}
|
|
2306
|
-
// mark node as destroyed
|
|
2307
|
-
rS[1 /* NgtRendererClassId.destroyed */] = true;
|
|
2308
|
-
}
|
|
2309
|
-
|
|
2310
|
-
const NGT_RENDERER_OPTIONS = new InjectionToken('NGT_RENDERER_OPTIONS');
|
|
2311
|
-
class NgtRendererFactory2 {
|
|
2312
|
-
/**
|
|
2313
|
-
* NOTE: We use `useFactory` to instantiate `NgtRendererFactory2`
|
|
2314
|
-
*/
|
|
2315
|
-
constructor(delegateRendererFactory) {
|
|
2316
|
-
this.delegateRendererFactory = delegateRendererFactory;
|
|
2317
|
-
this.catalogue = injectCatalogue();
|
|
2318
|
-
this.document = inject(DOCUMENT);
|
|
2319
|
-
this.options = inject(NGT_RENDERER_OPTIONS, { optional: true }) || {};
|
|
2320
|
-
this.rendererMap = new Map();
|
|
2321
|
-
}
|
|
2322
|
-
createRenderer(hostElement, type) {
|
|
2323
|
-
const delegateRenderer = this.delegateRendererFactory.createRenderer(hostElement, type);
|
|
2324
|
-
if (!type)
|
|
2325
|
-
return delegateRenderer;
|
|
2326
|
-
let renderer = this.rendererMap.get(type.id);
|
|
2327
|
-
if (renderer) {
|
|
2328
|
-
if (renderer instanceof NgtRenderer2) {
|
|
2329
|
-
renderer.count += 1;
|
|
2330
|
-
if (renderer.delegateRenderer !== delegateRenderer) {
|
|
2331
|
-
renderer.delegateRenderer = delegateRenderer;
|
|
2332
|
-
}
|
|
1702
|
+
if (pRS[0 /* NgtRendererClassId.type */] === 'portal' && cRS[0 /* NgtRendererClassId.type */] === 'three') {
|
|
1703
|
+
if (!cRS[5 /* NgtRendererClassId.parent */] && pRS[3 /* NgtRendererClassId.portalContainer */]) {
|
|
1704
|
+
return this.appendChild(pRS[3 /* NgtRendererClassId.portalContainer */], newChild);
|
|
2333
1705
|
}
|
|
2334
|
-
return
|
|
2335
|
-
}
|
|
2336
|
-
if (hostElement && !isRendererNode(hostElement)) {
|
|
2337
|
-
createRendererNode('platform', hostElement, this.document);
|
|
1706
|
+
return;
|
|
2338
1707
|
}
|
|
2339
|
-
if (
|
|
2340
|
-
this.
|
|
2341
|
-
// patch delegate destroyNode so we can destroy this HTML node
|
|
2342
|
-
// TODO: make sure we really need to do this
|
|
2343
|
-
const originalDestroyNode = delegateRenderer.destroyNode?.bind(delegateRenderer);
|
|
2344
|
-
if (!originalDestroyNode || !(NGT_DELEGATE_RENDERER_DESTROY_NODE_PATCHED_FLAG in originalDestroyNode)) {
|
|
2345
|
-
delegateRenderer.destroyNode = (node) => {
|
|
2346
|
-
originalDestroyNode?.(node);
|
|
2347
|
-
if (node !== hostElement)
|
|
2348
|
-
return;
|
|
2349
|
-
internalDestroyNode(node, null);
|
|
2350
|
-
};
|
|
2351
|
-
Object.assign(delegateRenderer.destroyNode, {
|
|
2352
|
-
[NGT_DELEGATE_RENDERER_DESTROY_NODE_PATCHED_FLAG]: true,
|
|
2353
|
-
});
|
|
2354
|
-
}
|
|
2355
|
-
return delegateRenderer;
|
|
1708
|
+
if (pRS[0 /* NgtRendererClassId.type */] === 'platform' && cRS[0 /* NgtRendererClassId.type */] === 'portal') {
|
|
1709
|
+
return this.delegateRenderer.appendChild(parent, newChild);
|
|
2356
1710
|
}
|
|
2357
|
-
|
|
2358
|
-
return renderer;
|
|
1711
|
+
return delegatedFn();
|
|
2359
1712
|
}
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
this.catalogue = catalogue;
|
|
2370
|
-
this.document = document;
|
|
2371
|
-
this.options = options;
|
|
2372
|
-
this.count = count;
|
|
2373
|
-
this.argsInjectors = [];
|
|
2374
|
-
this.parentInjectors = [];
|
|
2375
|
-
this.destroyNode = (node) => {
|
|
2376
|
-
internalDestroyNode(node, this.removeChild.bind(this));
|
|
2377
|
-
};
|
|
2378
|
-
this.addClass = this.delegateRenderer.addClass.bind(this.delegateRenderer);
|
|
2379
|
-
this.removeClass = this.delegateRenderer.removeClass.bind(this.delegateRenderer);
|
|
2380
|
-
this.setStyle = this.delegateRenderer.setStyle.bind(this.delegateRenderer);
|
|
2381
|
-
this.removeStyle = this.delegateRenderer.removeStyle.bind(this.delegateRenderer);
|
|
2382
|
-
this.selectRootElement = this.delegateRenderer.selectRootElement.bind(this.delegateRenderer);
|
|
2383
|
-
this.nextSibling = this.delegateRenderer.nextSibling.bind(this.delegateRenderer);
|
|
2384
|
-
this.setValue = this.delegateRenderer.setValue.bind(this.delegateRenderer);
|
|
2385
|
-
if (!this.options.verbose) {
|
|
2386
|
-
this.options.verbose = false;
|
|
1713
|
+
insertBefore(parent, newChild, refChild, isMove) {
|
|
1714
|
+
// if both are comments and the reference child is NgtCanvasContent, we'll assign the same flag to the newChild
|
|
1715
|
+
// this means that the NgtCanvas component is embedding. This flag allows the Renderer to get the root scene
|
|
1716
|
+
// when it tries to attach the template under `ng-template[canvasContent]`
|
|
1717
|
+
if (refChild &&
|
|
1718
|
+
refChild[NGT_CANVAS_CONTENT_FLAG] &&
|
|
1719
|
+
refChild instanceof Comment &&
|
|
1720
|
+
newChild instanceof Comment) {
|
|
1721
|
+
Object.assign(newChild, { [NGT_CANVAS_CONTENT_FLAG]: refChild[NGT_CANVAS_CONTENT_FLAG] });
|
|
2387
1722
|
}
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
}
|
|
2392
|
-
destroy() {
|
|
2393
|
-
if (this.count > 1) {
|
|
2394
|
-
this.count -= 1;
|
|
2395
|
-
return;
|
|
1723
|
+
// if there is no parent, we delegate
|
|
1724
|
+
if (!parent) {
|
|
1725
|
+
return this.delegateRenderer.insertBefore(parent, newChild, refChild, isMove);
|
|
2396
1726
|
}
|
|
2397
|
-
|
|
2398
|
-
this.count = 0;
|
|
2399
|
-
this.argsInjectors = [];
|
|
2400
|
-
this.parentInjectors = [];
|
|
1727
|
+
return this.appendChild(parent, newChild, refChild, isMove);
|
|
2401
1728
|
}
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
return createRendererNode('portal', platformElement, this.document);
|
|
2406
|
-
}
|
|
2407
|
-
if (name === 'ngt-value') {
|
|
2408
|
-
return createRendererNode('three', prepare(platformElement, 'ngt-value'), this.document);
|
|
1729
|
+
removeChild(parent, oldChild, isHostElement) {
|
|
1730
|
+
if (parent === null) {
|
|
1731
|
+
parent = this.parentNode(oldChild);
|
|
2409
1732
|
}
|
|
2410
|
-
const
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
if (name === 'ngt-primitive') {
|
|
2415
|
-
if (!injectedArgs[0])
|
|
2416
|
-
throw new Error(`[NGT] ngt-primitive without args is invalid`);
|
|
2417
|
-
const object = injectedArgs[0];
|
|
2418
|
-
let instanceState = getInstanceState(object);
|
|
2419
|
-
if (!instanceState || instanceState.type !== 'ngt-primitive') {
|
|
2420
|
-
// if an object isn't already "prepared", we'll prepare it
|
|
2421
|
-
prepare(object, 'ngt-primitive', instanceState);
|
|
1733
|
+
const cRS = oldChild.__ngt_renderer__;
|
|
1734
|
+
if (!cRS) {
|
|
1735
|
+
try {
|
|
1736
|
+
return this.delegateRenderer.removeChild(parent, oldChild, isHostElement);
|
|
2422
1737
|
}
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
primitiveRendererNode.__ngt_renderer__[5 /* NgtRendererClassId.parent */] =
|
|
2426
|
-
injectedParent;
|
|
2427
|
-
}
|
|
2428
|
-
return primitiveRendererNode;
|
|
2429
|
-
}
|
|
2430
|
-
if (!name.startsWith('ngt-')) {
|
|
2431
|
-
return createRendererNode('platform', platformElement, this.document);
|
|
2432
|
-
}
|
|
2433
|
-
const threeName = kebabToPascal(name.startsWith('ngt-') ? name.slice(4) : name);
|
|
2434
|
-
let threeTarget = this.catalogue[threeName];
|
|
2435
|
-
if (!threeTarget && threeName in THREE) {
|
|
2436
|
-
const threeSymbol = THREE[threeName];
|
|
2437
|
-
if (typeof threeSymbol === 'function') {
|
|
2438
|
-
// we will attempt to prefill the catalogue with symbols from THREE
|
|
2439
|
-
threeTarget = this.catalogue[threeName] = threeSymbol;
|
|
2440
|
-
}
|
|
2441
|
-
}
|
|
2442
|
-
if (threeTarget) {
|
|
2443
|
-
const threeInstance = prepare(new threeTarget(...injectedArgs), name);
|
|
2444
|
-
const rendererNode = createRendererNode('three', threeInstance, this.document);
|
|
2445
|
-
// assert type here because it is just created so we don't have to null check it
|
|
2446
|
-
const instanceState = getInstanceState(threeInstance);
|
|
2447
|
-
// auto-attach for geometry and material
|
|
2448
|
-
if (is.three(threeInstance, 'isBufferGeometry')) {
|
|
2449
|
-
instanceState.attach = ['geometry'];
|
|
2450
|
-
}
|
|
2451
|
-
else if (is.three(threeInstance, 'isMaterial')) {
|
|
2452
|
-
instanceState.attach = ['material'];
|
|
2453
|
-
}
|
|
2454
|
-
if (injectedParent) {
|
|
2455
|
-
rendererNode.__ngt_renderer__[5 /* NgtRendererClassId.parent */] =
|
|
2456
|
-
injectedParent;
|
|
2457
|
-
}
|
|
2458
|
-
return rendererNode;
|
|
2459
|
-
}
|
|
2460
|
-
return createRendererNode('platform', platformElement, this.document);
|
|
2461
|
-
}
|
|
2462
|
-
createComment(value) {
|
|
2463
|
-
const commentNode = this.delegateRenderer.createComment(value);
|
|
2464
|
-
const commentRendererNode = createRendererNode('comment', commentNode, this.document);
|
|
2465
|
-
// NOTE: we attach an arrow function to the Comment node
|
|
2466
|
-
// In our directives, we can call this function to then start tracking the RendererNode
|
|
2467
|
-
// this is done to limit the amount of Nodes we need to process for getCreationState
|
|
2468
|
-
Object.assign(commentRendererNode, {
|
|
2469
|
-
[NGT_INTERNAL_ADD_COMMENT_FLAG]: (type, injector) => {
|
|
2470
|
-
if (type === 'args') {
|
|
2471
|
-
this.argsInjectors.push(injector);
|
|
2472
|
-
}
|
|
2473
|
-
else if (type === 'parent') {
|
|
2474
|
-
Object.assign(commentRendererNode, {
|
|
2475
|
-
[NGT_INTERNAL_SET_PARENT_COMMENT_FLAG]: (ngtParent) => {
|
|
2476
|
-
commentRendererNode.__ngt_renderer__[5 /* NgtRendererClassId.parent */] = ngtParent;
|
|
2477
|
-
},
|
|
2478
|
-
});
|
|
2479
|
-
this.parentInjectors.push(injector);
|
|
2480
|
-
}
|
|
2481
|
-
commentRendererNode.__ngt_renderer__[4 /* NgtRendererClassId.injector */] = injector;
|
|
2482
|
-
},
|
|
2483
|
-
});
|
|
2484
|
-
return commentRendererNode;
|
|
2485
|
-
}
|
|
2486
|
-
createText(value) {
|
|
2487
|
-
const textNode = this.delegateRenderer.createText(value);
|
|
2488
|
-
return createRendererNode('text', textNode, this.document);
|
|
2489
|
-
}
|
|
2490
|
-
appendChild(parent, newChild, refChild, isMove) {
|
|
2491
|
-
const delegatedFn = refChild
|
|
2492
|
-
? this.delegateRenderer.insertBefore.bind(this.delegateRenderer, parent, newChild, refChild, isMove)
|
|
2493
|
-
: this.delegateRenderer.appendChild.bind(this.delegateRenderer, parent, newChild);
|
|
2494
|
-
const pRS = parent.__ngt_renderer__;
|
|
2495
|
-
const cRS = newChild.__ngt_renderer__;
|
|
2496
|
-
if (!pRS || !cRS) {
|
|
2497
|
-
this.options.verbose &&
|
|
2498
|
-
console.warn('[NGT dev mode] One of parent or child is not a renderer node.', { parent, newChild });
|
|
2499
|
-
return delegatedFn();
|
|
2500
|
-
}
|
|
2501
|
-
if (cRS[0 /* NgtRendererClassId.type */] === 'comment') {
|
|
2502
|
-
// if child is a comment, we'll set the parent then bail.
|
|
2503
|
-
// comment usually means it's part of a templateRef ViewContainerRef or structural directive
|
|
2504
|
-
setRendererParentNode(newChild, parent);
|
|
2505
|
-
// if parent is not three, we'll delegate to the renderer
|
|
2506
|
-
if (pRS[0 /* NgtRendererClassId.type */] !== 'three') {
|
|
2507
|
-
delegatedFn();
|
|
2508
|
-
}
|
|
2509
|
-
return;
|
|
2510
|
-
}
|
|
2511
|
-
if (pRS[0 /* NgtRendererClassId.type */] === 'platform' && cRS[0 /* NgtRendererClassId.type */] === 'platform') {
|
|
2512
|
-
if (newChild[NGT_DOM_PARENT_FLAG] && newChild[NGT_DOM_PARENT_FLAG] instanceof HTMLElement) {
|
|
2513
|
-
return this.delegateRenderer.appendChild(newChild[NGT_DOM_PARENT_FLAG], newChild);
|
|
2514
|
-
}
|
|
2515
|
-
if (pRS[5 /* NgtRendererClassId.parent */] && !cRS[5 /* NgtRendererClassId.parent */]) {
|
|
2516
|
-
return this.appendChild(pRS[5 /* NgtRendererClassId.parent */], newChild);
|
|
2517
|
-
}
|
|
2518
|
-
return delegatedFn();
|
|
2519
|
-
}
|
|
2520
|
-
if (pRS[0 /* NgtRendererClassId.type */] === 'three' && cRS[0 /* NgtRendererClassId.type */] === 'three') {
|
|
2521
|
-
return this.appendThreeRendererNodes(parent, newChild);
|
|
2522
|
-
}
|
|
2523
|
-
if (pRS[0 /* NgtRendererClassId.type */] === 'platform' && cRS[0 /* NgtRendererClassId.type */] === 'three') {
|
|
2524
|
-
// if platform has parent, delegate to that parent
|
|
2525
|
-
if (pRS[5 /* NgtRendererClassId.parent */]) {
|
|
2526
|
-
// but track the child for this parent as well
|
|
2527
|
-
addRendererChildNode(parent, newChild);
|
|
2528
|
-
return this.appendChild(pRS[5 /* NgtRendererClassId.parent */], newChild);
|
|
2529
|
-
}
|
|
2530
|
-
// platform can also have normal parentNode
|
|
2531
|
-
const platformParentNode = this.delegateRenderer.parentNode(parent);
|
|
2532
|
-
if (platformParentNode) {
|
|
2533
|
-
return this.appendChild(platformParentNode, newChild);
|
|
2534
|
-
}
|
|
2535
|
-
// if not, set up parent and child relationship for this pair then bail
|
|
2536
|
-
this.setNodeRelationship(parent, newChild);
|
|
2537
|
-
return;
|
|
2538
|
-
}
|
|
2539
|
-
if (pRS[0 /* NgtRendererClassId.type */] === 'three' && cRS[0 /* NgtRendererClassId.type */] === 'platform') {
|
|
2540
|
-
if (!cRS[5 /* NgtRendererClassId.parent */]) {
|
|
2541
|
-
setRendererParentNode(newChild, parent);
|
|
2542
|
-
}
|
|
2543
|
-
for (const child of cRS[6 /* NgtRendererClassId.children */]) {
|
|
2544
|
-
this.appendChild(parent, child);
|
|
2545
|
-
}
|
|
2546
|
-
for (const platformChildNode of newChild['childNodes'] || []) {
|
|
2547
|
-
if (!isRendererNode(platformChildNode) ||
|
|
2548
|
-
platformChildNode.__ngt_renderer__[0 /* NgtRendererClassId.type */] !== 'platform')
|
|
2549
|
-
continue;
|
|
2550
|
-
this.appendChild(parent, platformChildNode);
|
|
2551
|
-
}
|
|
2552
|
-
return;
|
|
2553
|
-
}
|
|
2554
|
-
if (pRS[0 /* NgtRendererClassId.type */] === 'portal' && cRS[0 /* NgtRendererClassId.type */] === 'three') {
|
|
2555
|
-
if (!cRS[5 /* NgtRendererClassId.parent */] && pRS[3 /* NgtRendererClassId.portalContainer */]) {
|
|
2556
|
-
return this.appendChild(pRS[3 /* NgtRendererClassId.portalContainer */], newChild);
|
|
2557
|
-
}
|
|
2558
|
-
return;
|
|
2559
|
-
}
|
|
2560
|
-
if (pRS[0 /* NgtRendererClassId.type */] === 'platform' && cRS[0 /* NgtRendererClassId.type */] === 'portal') {
|
|
2561
|
-
return this.delegateRenderer.appendChild(parent, newChild);
|
|
2562
|
-
}
|
|
2563
|
-
return delegatedFn();
|
|
2564
|
-
}
|
|
2565
|
-
insertBefore(parent, newChild, refChild, isMove) {
|
|
2566
|
-
// if both are comments and the reference child is NgtCanvasContent, we'll assign the same flag to the newChild
|
|
2567
|
-
// this means that the NgtCanvas component is embedding. This flag allows the Renderer to get the root scene
|
|
2568
|
-
// when it tries to attach the template under `ng-template[canvasContent]`
|
|
2569
|
-
if (refChild &&
|
|
2570
|
-
refChild[NGT_CANVAS_CONTENT_FLAG] &&
|
|
2571
|
-
refChild instanceof Comment &&
|
|
2572
|
-
newChild instanceof Comment) {
|
|
2573
|
-
Object.assign(newChild, { [NGT_CANVAS_CONTENT_FLAG]: refChild[NGT_CANVAS_CONTENT_FLAG] });
|
|
2574
|
-
}
|
|
2575
|
-
// if there is no parent, we delegate
|
|
2576
|
-
if (!parent) {
|
|
2577
|
-
return this.delegateRenderer.insertBefore(parent, newChild, refChild, isMove);
|
|
2578
|
-
}
|
|
2579
|
-
return this.appendChild(parent, newChild, refChild, isMove);
|
|
2580
|
-
}
|
|
2581
|
-
removeChild(parent, oldChild, isHostElement) {
|
|
2582
|
-
if (parent === null) {
|
|
2583
|
-
parent = this.parentNode(oldChild);
|
|
2584
|
-
}
|
|
2585
|
-
const cRS = oldChild.__ngt_renderer__;
|
|
2586
|
-
if (!cRS) {
|
|
2587
|
-
try {
|
|
2588
|
-
return this.delegateRenderer.removeChild(parent, oldChild, isHostElement);
|
|
2589
|
-
}
|
|
2590
|
-
catch {
|
|
2591
|
-
return;
|
|
1738
|
+
catch {
|
|
1739
|
+
return;
|
|
2592
1740
|
}
|
|
2593
1741
|
}
|
|
2594
1742
|
// disassociate things from oldChild
|
|
@@ -2882,6 +2030,907 @@ class NgtRenderer2 {
|
|
|
2882
2030
|
}
|
|
2883
2031
|
}
|
|
2884
2032
|
|
|
2033
|
+
function storeFactory() {
|
|
2034
|
+
const { invalidate, advance } = injectLoop();
|
|
2035
|
+
const rendererOptions = inject(NGT_RENDERER_OPTIONS, { optional: true }) || {};
|
|
2036
|
+
const document = inject(DOCUMENT);
|
|
2037
|
+
const window = document.defaultView || undefined;
|
|
2038
|
+
// NOTE: using Subject because we do not care about late-subscribers
|
|
2039
|
+
const pointerMissed$ = new Subject();
|
|
2040
|
+
const position = new THREE.Vector3();
|
|
2041
|
+
const defaultTarget = new THREE.Vector3();
|
|
2042
|
+
const tempTarget = new THREE.Vector3();
|
|
2043
|
+
let performanceTimeout = undefined;
|
|
2044
|
+
const pointer = new THREE.Vector2();
|
|
2045
|
+
// getCurrentViewport will mutate this instead of creating a new object everytime
|
|
2046
|
+
const tempViewport = {
|
|
2047
|
+
width: 0,
|
|
2048
|
+
height: 0,
|
|
2049
|
+
top: 0,
|
|
2050
|
+
left: 0,
|
|
2051
|
+
factor: 1,
|
|
2052
|
+
distance: 0,
|
|
2053
|
+
aspect: 0,
|
|
2054
|
+
};
|
|
2055
|
+
const store = signalState({
|
|
2056
|
+
id: makeId(),
|
|
2057
|
+
maxNotificationSkipCount: rendererOptions.maxNotificationSkipCount || 5,
|
|
2058
|
+
pointerMissed$: pointerMissed$.asObservable(),
|
|
2059
|
+
events: { priority: 1, enabled: true, connected: false },
|
|
2060
|
+
// Mock objects that have to be configured
|
|
2061
|
+
gl: null,
|
|
2062
|
+
camera: null,
|
|
2063
|
+
raycaster: null,
|
|
2064
|
+
scene: null,
|
|
2065
|
+
xr: null,
|
|
2066
|
+
invalidate: (frames = 1) => invalidate(store, frames),
|
|
2067
|
+
advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, store),
|
|
2068
|
+
legacy: false,
|
|
2069
|
+
linear: false,
|
|
2070
|
+
flat: false,
|
|
2071
|
+
controls: null,
|
|
2072
|
+
clock: new THREE.Clock(),
|
|
2073
|
+
pointer,
|
|
2074
|
+
frameloop: 'always',
|
|
2075
|
+
performance: {
|
|
2076
|
+
current: 1,
|
|
2077
|
+
min: 0.5,
|
|
2078
|
+
max: 1,
|
|
2079
|
+
debounce: 200,
|
|
2080
|
+
regress: () => {
|
|
2081
|
+
const state = store.snapshot;
|
|
2082
|
+
// Clear timeout
|
|
2083
|
+
if (performanceTimeout)
|
|
2084
|
+
clearTimeout(performanceTimeout);
|
|
2085
|
+
// Set lower bound performance
|
|
2086
|
+
if (state.performance.current !== state.performance.min)
|
|
2087
|
+
store.update((state) => ({
|
|
2088
|
+
performance: { ...state.performance, current: state.performance.min },
|
|
2089
|
+
}));
|
|
2090
|
+
// Go back to upper bound performance after a while unless something regresses meanwhile
|
|
2091
|
+
performanceTimeout = setTimeout(() => store.update((state) => ({
|
|
2092
|
+
performance: { ...state.performance, current: store.snapshot.performance.max },
|
|
2093
|
+
})), state.performance.debounce);
|
|
2094
|
+
},
|
|
2095
|
+
},
|
|
2096
|
+
size: { width: 0, height: 0, top: 0, left: 0 },
|
|
2097
|
+
viewport: {
|
|
2098
|
+
initialDpr: window?.devicePixelRatio || 1,
|
|
2099
|
+
dpr: window?.devicePixelRatio || 1,
|
|
2100
|
+
width: 0,
|
|
2101
|
+
height: 0,
|
|
2102
|
+
top: 0,
|
|
2103
|
+
left: 0,
|
|
2104
|
+
aspect: 0,
|
|
2105
|
+
distance: 0,
|
|
2106
|
+
factor: 0,
|
|
2107
|
+
getCurrentViewport(camera = store.snapshot.camera, target = defaultTarget, size = store.snapshot.size) {
|
|
2108
|
+
const { width, height, top, left } = size;
|
|
2109
|
+
const aspect = width / height;
|
|
2110
|
+
if (target.isVector3)
|
|
2111
|
+
tempTarget.copy(target);
|
|
2112
|
+
else
|
|
2113
|
+
tempTarget.set(...target);
|
|
2114
|
+
const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
|
|
2115
|
+
// Update the pre-allocated viewport object
|
|
2116
|
+
tempViewport.top = top;
|
|
2117
|
+
tempViewport.left = left;
|
|
2118
|
+
tempViewport.aspect = aspect;
|
|
2119
|
+
tempViewport.distance = distance;
|
|
2120
|
+
if (is.three(camera, 'isOrthographicCamera')) {
|
|
2121
|
+
// For orthographic cameras
|
|
2122
|
+
tempViewport.width = width / camera.zoom;
|
|
2123
|
+
tempViewport.height = height / camera.zoom;
|
|
2124
|
+
tempViewport.factor = 1;
|
|
2125
|
+
}
|
|
2126
|
+
else {
|
|
2127
|
+
// For perspective cameras
|
|
2128
|
+
const fov = (camera.fov * Math.PI) / 180; // convert vertical fov to radians
|
|
2129
|
+
const h = 2 * Math.tan(fov / 2) * distance; // visible height
|
|
2130
|
+
const w = h * aspect; // visible width
|
|
2131
|
+
tempViewport.width = w;
|
|
2132
|
+
tempViewport.height = h;
|
|
2133
|
+
tempViewport.factor = width / w;
|
|
2134
|
+
}
|
|
2135
|
+
return tempViewport;
|
|
2136
|
+
},
|
|
2137
|
+
},
|
|
2138
|
+
setEvents: (events) => store.update((state) => ({ events: { ...state.events, ...events } })),
|
|
2139
|
+
setSize: (width, height, top, left) => {
|
|
2140
|
+
const camera = store.snapshot.camera;
|
|
2141
|
+
const size = { width, height, top: top ?? 0, left: left ?? 0 };
|
|
2142
|
+
store.update((state) => ({
|
|
2143
|
+
size,
|
|
2144
|
+
viewport: {
|
|
2145
|
+
...state.viewport,
|
|
2146
|
+
...state.viewport.getCurrentViewport(camera, defaultTarget, size),
|
|
2147
|
+
},
|
|
2148
|
+
}));
|
|
2149
|
+
},
|
|
2150
|
+
setDpr: (dpr) => {
|
|
2151
|
+
const resolved = makeDpr(dpr, window);
|
|
2152
|
+
store.update((state) => ({
|
|
2153
|
+
viewport: { ...state.viewport, dpr: resolved, initialDpr: state.viewport.initialDpr || resolved },
|
|
2154
|
+
}));
|
|
2155
|
+
},
|
|
2156
|
+
setFrameloop: (frameloop) => {
|
|
2157
|
+
const clock = store.snapshot.clock;
|
|
2158
|
+
// if frameloop === "never" clock.elapsedTime is updated using advance(timestamp)
|
|
2159
|
+
clock.stop();
|
|
2160
|
+
clock.elapsedTime = 0;
|
|
2161
|
+
if (frameloop !== 'never') {
|
|
2162
|
+
clock.start();
|
|
2163
|
+
clock.elapsedTime = 0;
|
|
2164
|
+
}
|
|
2165
|
+
store.update(() => ({ frameloop }));
|
|
2166
|
+
},
|
|
2167
|
+
previousRoot: null,
|
|
2168
|
+
internal: {
|
|
2169
|
+
active: false,
|
|
2170
|
+
priority: 0,
|
|
2171
|
+
frames: 0,
|
|
2172
|
+
lastEvent: new ElementRef(null),
|
|
2173
|
+
interaction: [],
|
|
2174
|
+
hovered: new Map(),
|
|
2175
|
+
capturedMap: new Map(),
|
|
2176
|
+
initialClick: [0, 0],
|
|
2177
|
+
initialHits: [],
|
|
2178
|
+
subscribers: [],
|
|
2179
|
+
subscribe: (callback, priority = 0, _store = store) => {
|
|
2180
|
+
const internal = _store.snapshot.internal;
|
|
2181
|
+
// If this subscription was given a priority, it takes rendering into its own hands
|
|
2182
|
+
// For that reason we switch off automatic rendering and increase the manual flag
|
|
2183
|
+
// As long as this flag is positive there can be no internal rendering at all
|
|
2184
|
+
// because there could be multiple render subscriptions
|
|
2185
|
+
internal.priority = internal.priority + (priority > 0 ? 1 : 0);
|
|
2186
|
+
internal.subscribers.push({ callback, priority, store: _store });
|
|
2187
|
+
// Register subscriber and sort layers from lowest to highest, meaning,
|
|
2188
|
+
// highest priority renders last (on top of the other frames)
|
|
2189
|
+
internal.subscribers = internal.subscribers.sort((a, b) => (a.priority || 0) - (b.priority || 0));
|
|
2190
|
+
return () => {
|
|
2191
|
+
const internal = _store.snapshot.internal;
|
|
2192
|
+
if (internal?.subscribers) {
|
|
2193
|
+
// Decrease manual flag if this subscription had a priority
|
|
2194
|
+
internal.priority = internal.priority - (priority > 0 ? 1 : 0);
|
|
2195
|
+
// Remove subscriber from list
|
|
2196
|
+
internal.subscribers = internal.subscribers.filter((s) => s.callback !== callback);
|
|
2197
|
+
}
|
|
2198
|
+
};
|
|
2199
|
+
},
|
|
2200
|
+
},
|
|
2201
|
+
});
|
|
2202
|
+
Object.defineProperty(store, '__pointerMissed$', { get: () => pointerMissed$ });
|
|
2203
|
+
let { size: oldSize, viewport: { dpr: oldDpr }, camera: oldCamera, } = store.snapshot;
|
|
2204
|
+
effect(() => {
|
|
2205
|
+
const [newCamera, newSize, newDpr, gl] = [store.camera(), store.size(), store.viewport.dpr(), store.gl()];
|
|
2206
|
+
// Resize camera and renderer on changes to size and pixel-ratio
|
|
2207
|
+
if (newSize !== oldSize || newDpr !== oldDpr) {
|
|
2208
|
+
oldSize = newSize;
|
|
2209
|
+
oldDpr = newDpr;
|
|
2210
|
+
// Update camera & renderer
|
|
2211
|
+
updateCamera(newCamera, newSize);
|
|
2212
|
+
gl.setPixelRatio(newDpr);
|
|
2213
|
+
const updateStyle = typeof HTMLCanvasElement !== 'undefined' && gl.domElement instanceof HTMLCanvasElement;
|
|
2214
|
+
gl.setSize(newSize.width, newSize.height, updateStyle);
|
|
2215
|
+
}
|
|
2216
|
+
// Update viewport once the camera changes
|
|
2217
|
+
if (newCamera !== oldCamera) {
|
|
2218
|
+
oldCamera = newCamera;
|
|
2219
|
+
updateCamera(newCamera, newSize);
|
|
2220
|
+
// Update viewport
|
|
2221
|
+
store.update((state) => ({
|
|
2222
|
+
viewport: { ...state.viewport, ...state.viewport.getCurrentViewport(newCamera) },
|
|
2223
|
+
}));
|
|
2224
|
+
}
|
|
2225
|
+
});
|
|
2226
|
+
return store;
|
|
2227
|
+
}
|
|
2228
|
+
const NGT_STORE = new InjectionToken('NgtStore Token');
|
|
2229
|
+
function injectStore(options) {
|
|
2230
|
+
return inject(NGT_STORE, options);
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2233
|
+
function resolveRef(ref) {
|
|
2234
|
+
if (is.ref(ref)) {
|
|
2235
|
+
return ref.nativeElement;
|
|
2236
|
+
}
|
|
2237
|
+
return ref;
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
class NgtParent extends NgtCommonDirective {
|
|
2241
|
+
constructor() {
|
|
2242
|
+
super();
|
|
2243
|
+
this.parent = input.required();
|
|
2244
|
+
this.store = injectStore();
|
|
2245
|
+
this._parent = computed(() => {
|
|
2246
|
+
const parent = this.parent();
|
|
2247
|
+
const rawParent = typeof parent === 'function' ? parent() : parent;
|
|
2248
|
+
if (!rawParent)
|
|
2249
|
+
return null;
|
|
2250
|
+
const scene = this.store.scene();
|
|
2251
|
+
if (typeof rawParent === 'string') {
|
|
2252
|
+
return scene.getObjectByName(rawParent);
|
|
2253
|
+
}
|
|
2254
|
+
return resolveRef(rawParent);
|
|
2255
|
+
});
|
|
2256
|
+
this.linkedValue = linkedSignal(this._parent);
|
|
2257
|
+
this.shouldSkipRender = computed(() => !this._parent());
|
|
2258
|
+
const commentNode = this.commentNode;
|
|
2259
|
+
commentNode.data = NGT_PARENT_FLAG;
|
|
2260
|
+
commentNode[NGT_PARENT_FLAG] = true;
|
|
2261
|
+
if (commentNode[NGT_INTERNAL_ADD_COMMENT_FLAG]) {
|
|
2262
|
+
commentNode[NGT_INTERNAL_ADD_COMMENT_FLAG]('parent', this.injector);
|
|
2263
|
+
delete commentNode[NGT_INTERNAL_ADD_COMMENT_FLAG];
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
validate() {
|
|
2267
|
+
return !this.injected && !!this.injectedValue;
|
|
2268
|
+
}
|
|
2269
|
+
beforeCreateView() {
|
|
2270
|
+
const commentNode = this.commentNode;
|
|
2271
|
+
if (commentNode[NGT_INTERNAL_SET_PARENT_COMMENT_FLAG]) {
|
|
2272
|
+
commentNode[NGT_INTERNAL_SET_PARENT_COMMENT_FLAG](this.injectedValue);
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgtParent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2276
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.11", type: NgtParent, isStandalone: true, selector: "ng-template[parent]", inputs: { parent: { classPropertyName: "parent", publicName: "parent", isSignal: true, isRequired: true, transformFunction: null } }, usesInheritance: true, ngImport: i0 }); }
|
|
2277
|
+
}
|
|
2278
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgtParent, decorators: [{
|
|
2279
|
+
type: Directive,
|
|
2280
|
+
args: [{ selector: 'ng-template[parent]' }]
|
|
2281
|
+
}], ctorParameters: () => [] });
|
|
2282
|
+
|
|
2283
|
+
class NgtSelectionApi {
|
|
2284
|
+
constructor() {
|
|
2285
|
+
this.enabled = input(true, { alias: 'selection', transform: booleanAttribute });
|
|
2286
|
+
this.source = signal([]);
|
|
2287
|
+
this.selected = this.source.asReadonly();
|
|
2288
|
+
}
|
|
2289
|
+
update(...args) {
|
|
2290
|
+
if (!this.enabled())
|
|
2291
|
+
return;
|
|
2292
|
+
this.source.update(...args);
|
|
2293
|
+
}
|
|
2294
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgtSelectionApi, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2295
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.11", type: NgtSelectionApi, isStandalone: true, selector: "[selection]", inputs: { enabled: { classPropertyName: "enabled", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
|
|
2296
|
+
}
|
|
2297
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgtSelectionApi, decorators: [{
|
|
2298
|
+
type: Directive,
|
|
2299
|
+
args: [{ selector: '[selection]' }]
|
|
2300
|
+
}] });
|
|
2301
|
+
class NgtSelect {
|
|
2302
|
+
constructor() {
|
|
2303
|
+
this.enabled = input(false, { transform: booleanAttribute, alias: 'select' });
|
|
2304
|
+
const elementRef = inject(ElementRef);
|
|
2305
|
+
const selectionApi = inject(NgtSelectionApi);
|
|
2306
|
+
effect((onCleanup) => {
|
|
2307
|
+
const selectionEnabled = selectionApi.enabled();
|
|
2308
|
+
if (!selectionEnabled)
|
|
2309
|
+
return;
|
|
2310
|
+
const enabled = this.enabled();
|
|
2311
|
+
if (!enabled)
|
|
2312
|
+
return;
|
|
2313
|
+
const host = elementRef.nativeElement;
|
|
2314
|
+
const localState = getInstanceState(host);
|
|
2315
|
+
if (!localState)
|
|
2316
|
+
return;
|
|
2317
|
+
// ngt-mesh[select]
|
|
2318
|
+
if (host.type === 'Mesh') {
|
|
2319
|
+
selectionApi.update((prev) => [...prev, host]);
|
|
2320
|
+
onCleanup(() => selectionApi.update((prev) => prev.filter((el) => el !== host)));
|
|
2321
|
+
return;
|
|
2322
|
+
}
|
|
2323
|
+
const [collection] = [untracked(selectionApi.selected), localState.objects()];
|
|
2324
|
+
let changed = false;
|
|
2325
|
+
const current = [];
|
|
2326
|
+
host.traverse((child) => {
|
|
2327
|
+
child.type === 'Mesh' && current.push(child);
|
|
2328
|
+
if (collection.indexOf(child) === -1)
|
|
2329
|
+
changed = true;
|
|
2330
|
+
});
|
|
2331
|
+
if (!changed)
|
|
2332
|
+
return;
|
|
2333
|
+
selectionApi.update((prev) => [...prev, ...current]);
|
|
2334
|
+
onCleanup(() => {
|
|
2335
|
+
selectionApi.update((prev) => prev.filter((el) => !current.includes(el)));
|
|
2336
|
+
});
|
|
2337
|
+
});
|
|
2338
|
+
}
|
|
2339
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgtSelect, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2340
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.11", type: NgtSelect, isStandalone: true, selector: "ngt-group[select], ngt-mesh[select]", inputs: { enabled: { classPropertyName: "enabled", publicName: "select", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
|
|
2341
|
+
}
|
|
2342
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgtSelect, decorators: [{
|
|
2343
|
+
type: Directive,
|
|
2344
|
+
args: [{ selector: 'ngt-group[select], ngt-mesh[select]' }]
|
|
2345
|
+
}], ctorParameters: () => [] });
|
|
2346
|
+
const NgtSelection = [NgtSelectionApi, NgtSelect];
|
|
2347
|
+
|
|
2348
|
+
var _a;
|
|
2349
|
+
const NGT_HTML_DOM_ELEMENT = new InjectionToken('NGT_HTML_DOM_ELEMENT');
|
|
2350
|
+
function provideHTMLDomElement(...args) {
|
|
2351
|
+
if (args.length === 0) {
|
|
2352
|
+
return { provide: NGT_HTML_DOM_ELEMENT, useFactory: () => 'gl' };
|
|
2353
|
+
}
|
|
2354
|
+
if (args.length === 1) {
|
|
2355
|
+
return { provide: NGT_HTML_DOM_ELEMENT, useFactory: args[0] };
|
|
2356
|
+
}
|
|
2357
|
+
return { provide: NGT_HTML_DOM_ELEMENT, useFactory: args.pop(), deps: args };
|
|
2358
|
+
}
|
|
2359
|
+
class NgtHTML {
|
|
2360
|
+
static { _a = NGT_HTML_FLAG; }
|
|
2361
|
+
static { this[_a] = true; }
|
|
2362
|
+
constructor() {
|
|
2363
|
+
this.domElement = inject(NGT_HTML_DOM_ELEMENT, { self: true, optional: true });
|
|
2364
|
+
const host = inject(ElementRef);
|
|
2365
|
+
const store = injectStore();
|
|
2366
|
+
if (this.domElement === 'gl') {
|
|
2367
|
+
Object.assign(host.nativeElement, {
|
|
2368
|
+
[NGT_DOM_PARENT_FLAG]: store.snapshot.gl.domElement.parentElement,
|
|
2369
|
+
});
|
|
2370
|
+
}
|
|
2371
|
+
else if (this.domElement) {
|
|
2372
|
+
Object.assign(host.nativeElement, { [NGT_DOM_PARENT_FLAG]: this.domElement });
|
|
2373
|
+
}
|
|
2374
|
+
inject(DestroyRef).onDestroy(() => {
|
|
2375
|
+
host.nativeElement[NGT_DOM_PARENT_FLAG] = null;
|
|
2376
|
+
delete host.nativeElement[NGT_DOM_PARENT_FLAG];
|
|
2377
|
+
});
|
|
2378
|
+
}
|
|
2379
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgtHTML, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2380
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.11", type: NgtHTML, isStandalone: true, ngImport: i0 }); }
|
|
2381
|
+
}
|
|
2382
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgtHTML, decorators: [{
|
|
2383
|
+
type: Directive
|
|
2384
|
+
}], ctorParameters: () => [] });
|
|
2385
|
+
|
|
2386
|
+
const cached$1 = new Map();
|
|
2387
|
+
const memoizedLoaders$1 = new WeakMap();
|
|
2388
|
+
function normalizeInputs$1(input) {
|
|
2389
|
+
let urls = [];
|
|
2390
|
+
if (Array.isArray(input)) {
|
|
2391
|
+
urls = input;
|
|
2392
|
+
}
|
|
2393
|
+
else if (typeof input === 'string') {
|
|
2394
|
+
urls = [input];
|
|
2395
|
+
}
|
|
2396
|
+
else {
|
|
2397
|
+
urls = Object.values(input);
|
|
2398
|
+
}
|
|
2399
|
+
return urls.map((url) => (url.includes('undefined') || url.includes('null') || !url ? '' : url));
|
|
2400
|
+
}
|
|
2401
|
+
function load(loaderConstructorFactory, inputs, { extensions, onLoad, onProgress, } = {}) {
|
|
2402
|
+
return () => {
|
|
2403
|
+
const urls = normalizeInputs$1(inputs());
|
|
2404
|
+
let loader = memoizedLoaders$1.get(loaderConstructorFactory(urls));
|
|
2405
|
+
if (!loader) {
|
|
2406
|
+
loader = new (loaderConstructorFactory(urls))();
|
|
2407
|
+
memoizedLoaders$1.set(loaderConstructorFactory(urls), loader);
|
|
2408
|
+
}
|
|
2409
|
+
if (extensions)
|
|
2410
|
+
extensions(loader);
|
|
2411
|
+
return urls.map((url) => {
|
|
2412
|
+
if (url === '')
|
|
2413
|
+
return Promise.resolve(null);
|
|
2414
|
+
if (!cached$1.has(url)) {
|
|
2415
|
+
cached$1.set(url, new Promise((resolve, reject) => {
|
|
2416
|
+
loader.load(url, (data) => {
|
|
2417
|
+
if ('scene' in data) {
|
|
2418
|
+
Object.assign(data, makeObjectGraph(data['scene']));
|
|
2419
|
+
}
|
|
2420
|
+
if (onLoad) {
|
|
2421
|
+
onLoad(data);
|
|
2422
|
+
}
|
|
2423
|
+
resolve(data);
|
|
2424
|
+
}, onProgress, (error) => reject(new Error(`[NGT] Could not load ${url}: ${error?.message}`)));
|
|
2425
|
+
}));
|
|
2426
|
+
}
|
|
2427
|
+
return cached$1.get(url);
|
|
2428
|
+
});
|
|
2429
|
+
};
|
|
2430
|
+
}
|
|
2431
|
+
/**
|
|
2432
|
+
* @deprecated Use loaderResource instead. Will be removed in v5.0.0
|
|
2433
|
+
* @since v4.0.0~
|
|
2434
|
+
*/
|
|
2435
|
+
function _injectLoader(loaderConstructorFactory, inputs, { extensions, onProgress, onLoad, injector, } = {}) {
|
|
2436
|
+
return assertInjector(_injectLoader, injector, () => {
|
|
2437
|
+
const response = signal(null);
|
|
2438
|
+
const cachedResultPromisesEffect = load(loaderConstructorFactory, inputs, {
|
|
2439
|
+
extensions,
|
|
2440
|
+
onProgress,
|
|
2441
|
+
onLoad: onLoad,
|
|
2442
|
+
});
|
|
2443
|
+
effect(() => {
|
|
2444
|
+
const originalUrls = inputs();
|
|
2445
|
+
const cachedResultPromises = cachedResultPromisesEffect();
|
|
2446
|
+
Promise.all(cachedResultPromises).then((results) => {
|
|
2447
|
+
response.update(() => {
|
|
2448
|
+
if (Array.isArray(originalUrls))
|
|
2449
|
+
return results;
|
|
2450
|
+
if (typeof originalUrls === 'string')
|
|
2451
|
+
return results[0];
|
|
2452
|
+
const keys = Object.keys(originalUrls);
|
|
2453
|
+
return keys.reduce((result, key) => {
|
|
2454
|
+
// @ts-ignore
|
|
2455
|
+
result[key] = results[keys.indexOf(key)];
|
|
2456
|
+
return result;
|
|
2457
|
+
}, {});
|
|
2458
|
+
});
|
|
2459
|
+
});
|
|
2460
|
+
});
|
|
2461
|
+
return response.asReadonly();
|
|
2462
|
+
});
|
|
2463
|
+
}
|
|
2464
|
+
_injectLoader.preload = (loaderConstructorFactory, inputs, extensions, onLoad) => {
|
|
2465
|
+
const effects = load(loaderConstructorFactory, inputs, { extensions, onLoad })();
|
|
2466
|
+
if (effects) {
|
|
2467
|
+
void Promise.all(effects);
|
|
2468
|
+
}
|
|
2469
|
+
};
|
|
2470
|
+
_injectLoader.destroy = () => {
|
|
2471
|
+
cached$1.clear();
|
|
2472
|
+
};
|
|
2473
|
+
_injectLoader.clear = (urls) => {
|
|
2474
|
+
const urlToClear = Array.isArray(urls) ? urls : [urls];
|
|
2475
|
+
urlToClear.forEach((url) => {
|
|
2476
|
+
cached$1.delete(url);
|
|
2477
|
+
});
|
|
2478
|
+
};
|
|
2479
|
+
/**
|
|
2480
|
+
* @deprecated Use loaderResource instead. Will be removed in v5.0.0
|
|
2481
|
+
* @since v4.0.0~
|
|
2482
|
+
*/
|
|
2483
|
+
const injectLoader = _injectLoader;
|
|
2484
|
+
|
|
2485
|
+
function normalizeInputs(input) {
|
|
2486
|
+
let urls = [];
|
|
2487
|
+
if (Array.isArray(input)) {
|
|
2488
|
+
urls = input;
|
|
2489
|
+
}
|
|
2490
|
+
else if (typeof input === 'string') {
|
|
2491
|
+
urls = [input];
|
|
2492
|
+
}
|
|
2493
|
+
else {
|
|
2494
|
+
urls = Object.values(input);
|
|
2495
|
+
}
|
|
2496
|
+
return urls.map((url) => (url.includes('undefined') || url.includes('null') || !url ? '' : url));
|
|
2497
|
+
}
|
|
2498
|
+
const cached = new Map();
|
|
2499
|
+
const memoizedLoaders = new WeakMap();
|
|
2500
|
+
function getLoaderRequestParams(input, loaderConstructorFactory, extensions) {
|
|
2501
|
+
const urls = input();
|
|
2502
|
+
const LoaderConstructor = loaderConstructorFactory(urls);
|
|
2503
|
+
const normalizedUrls = normalizeInputs(urls);
|
|
2504
|
+
let loader = memoizedLoaders.get(LoaderConstructor);
|
|
2505
|
+
if (!loader) {
|
|
2506
|
+
loader = new LoaderConstructor();
|
|
2507
|
+
memoizedLoaders.set(LoaderConstructor, loader);
|
|
2508
|
+
}
|
|
2509
|
+
if (extensions)
|
|
2510
|
+
extensions(loader);
|
|
2511
|
+
return { urls, normalizedUrls, loader };
|
|
2512
|
+
}
|
|
2513
|
+
function getLoaderPromises(request, onProgress) {
|
|
2514
|
+
return request.normalizedUrls.map((url) => {
|
|
2515
|
+
if (url === '')
|
|
2516
|
+
return Promise.resolve(null);
|
|
2517
|
+
const cachedPromise = cached.get(url);
|
|
2518
|
+
if (cachedPromise)
|
|
2519
|
+
return cachedPromise;
|
|
2520
|
+
const promise = new Promise((res, rej) => {
|
|
2521
|
+
request.loader.load(url, (data) => {
|
|
2522
|
+
if ('scene' in data) {
|
|
2523
|
+
Object.assign(data, makeObjectGraph(data['scene']));
|
|
2524
|
+
}
|
|
2525
|
+
res(data);
|
|
2526
|
+
}, onProgress, (error) => rej(new Error(`[NGT] Could not load ${url}: ${error?.message}`)));
|
|
2527
|
+
});
|
|
2528
|
+
cached.set(url, promise);
|
|
2529
|
+
return promise;
|
|
2530
|
+
});
|
|
2531
|
+
}
|
|
2532
|
+
function loaderResource(loaderConstructorFactory, input, { extensions, onLoad, onProgress, injector, } = {}) {
|
|
2533
|
+
return assertInjector(loaderResource, injector, () => {
|
|
2534
|
+
return resource({
|
|
2535
|
+
request: () => getLoaderRequestParams(input, loaderConstructorFactory, extensions),
|
|
2536
|
+
loader: async ({ request }) => {
|
|
2537
|
+
// TODO: use the abortSignal when THREE.Loader supports it
|
|
2538
|
+
const loadedResults = await Promise.all(getLoaderPromises(request, onProgress));
|
|
2539
|
+
let results;
|
|
2540
|
+
if (Array.isArray(request.urls)) {
|
|
2541
|
+
results = loadedResults;
|
|
2542
|
+
}
|
|
2543
|
+
else if (typeof request.urls === 'string') {
|
|
2544
|
+
results = loadedResults[0];
|
|
2545
|
+
}
|
|
2546
|
+
else {
|
|
2547
|
+
const keys = Object.keys(request.urls);
|
|
2548
|
+
results = keys.reduce((result, key) => {
|
|
2549
|
+
// @ts-ignore
|
|
2550
|
+
result[key] = loadedResults[keys.indexOf(key)];
|
|
2551
|
+
return result;
|
|
2552
|
+
}, {});
|
|
2553
|
+
}
|
|
2554
|
+
if (onLoad)
|
|
2555
|
+
onLoad(results);
|
|
2556
|
+
return results;
|
|
2557
|
+
},
|
|
2558
|
+
});
|
|
2559
|
+
});
|
|
2560
|
+
}
|
|
2561
|
+
loaderResource.preload = (loaderConstructor, inputs, extensions) => {
|
|
2562
|
+
const params = getLoaderRequestParams(() => inputs, () => loaderConstructor, extensions);
|
|
2563
|
+
void Promise.all(getLoaderPromises(params));
|
|
2564
|
+
};
|
|
2565
|
+
loaderResource.destroy = () => {
|
|
2566
|
+
cached.clear();
|
|
2567
|
+
};
|
|
2568
|
+
loaderResource.clear = (urls) => {
|
|
2569
|
+
const urlToClear = Array.isArray(urls) ? urls : [urls];
|
|
2570
|
+
urlToClear.forEach((url) => {
|
|
2571
|
+
cached.delete(url);
|
|
2572
|
+
});
|
|
2573
|
+
};
|
|
2574
|
+
|
|
2575
|
+
const RGBA_REGEX = /rgba?\((\d+),\s*(\d+),\s*(\d+),?\s*(\d*\.?\d+)?\)/;
|
|
2576
|
+
const DEFAULT_COLOR = 0x000000;
|
|
2577
|
+
class NgtHexify {
|
|
2578
|
+
constructor() {
|
|
2579
|
+
this.document = inject(DOCUMENT, { optional: true });
|
|
2580
|
+
this.cache = {};
|
|
2581
|
+
}
|
|
2582
|
+
/**
|
|
2583
|
+
* transforms a:
|
|
2584
|
+
* - hex string to a hex number
|
|
2585
|
+
* - rgb string to a hex number
|
|
2586
|
+
* - rgba string to a hex number
|
|
2587
|
+
* - css color string to a hex number
|
|
2588
|
+
*
|
|
2589
|
+
* always default to black if failed
|
|
2590
|
+
* @param value
|
|
2591
|
+
*/
|
|
2592
|
+
transform(value) {
|
|
2593
|
+
if (value == null)
|
|
2594
|
+
return DEFAULT_COLOR;
|
|
2595
|
+
if (value.startsWith('#')) {
|
|
2596
|
+
if (!this.cache[value]) {
|
|
2597
|
+
this.cache[value] = this.hexStringToNumber(value);
|
|
2598
|
+
}
|
|
2599
|
+
return this.cache[value];
|
|
2600
|
+
}
|
|
2601
|
+
if (!this.ctx) {
|
|
2602
|
+
this.ctx = this.document?.createElement('canvas').getContext('2d');
|
|
2603
|
+
}
|
|
2604
|
+
if (!this.ctx) {
|
|
2605
|
+
console.warn('[NGT] hexify: canvas context is not available');
|
|
2606
|
+
return DEFAULT_COLOR;
|
|
2607
|
+
}
|
|
2608
|
+
this.ctx.fillStyle = value;
|
|
2609
|
+
const computedValue = this.ctx.fillStyle;
|
|
2610
|
+
if (computedValue.startsWith('#')) {
|
|
2611
|
+
if (!this.cache[computedValue]) {
|
|
2612
|
+
this.cache[computedValue] = this.hexStringToNumber(computedValue);
|
|
2613
|
+
}
|
|
2614
|
+
return this.cache[computedValue];
|
|
2615
|
+
}
|
|
2616
|
+
if (!computedValue.startsWith('rgba')) {
|
|
2617
|
+
console.warn(`[NGT] hexify: invalid color format. Expected rgba or hex, receive: ${computedValue}`);
|
|
2618
|
+
return DEFAULT_COLOR;
|
|
2619
|
+
}
|
|
2620
|
+
const match = computedValue.match(RGBA_REGEX);
|
|
2621
|
+
if (!match) {
|
|
2622
|
+
console.warn(`[NGT] hexify: invalid color format. Expected rgba or hex, receive: ${computedValue}`);
|
|
2623
|
+
return DEFAULT_COLOR;
|
|
2624
|
+
}
|
|
2625
|
+
const r = parseInt(match[1], 10);
|
|
2626
|
+
const g = parseInt(match[2], 10);
|
|
2627
|
+
const b = parseInt(match[3], 10);
|
|
2628
|
+
const a = match[4] ? parseFloat(match[4]) : 1.0;
|
|
2629
|
+
const cacheKey = `${r}:${g}:${b}:${a}`;
|
|
2630
|
+
// check result from cache
|
|
2631
|
+
if (!this.cache[cacheKey]) {
|
|
2632
|
+
// Convert the components to hex strings
|
|
2633
|
+
const hexR = this.componentToHex(r);
|
|
2634
|
+
const hexG = this.componentToHex(g);
|
|
2635
|
+
const hexB = this.componentToHex(b);
|
|
2636
|
+
const hexA = this.componentToHex(Math.round(a * 255));
|
|
2637
|
+
// Combine the hex components into a single hex string
|
|
2638
|
+
const hex = `#${hexR}${hexG}${hexB}${hexA}`;
|
|
2639
|
+
this.cache[cacheKey] = this.hexStringToNumber(hex);
|
|
2640
|
+
}
|
|
2641
|
+
return this.cache[cacheKey];
|
|
2642
|
+
}
|
|
2643
|
+
hexStringToNumber(hexString) {
|
|
2644
|
+
return parseInt(hexString.replace('#', ''), 16);
|
|
2645
|
+
}
|
|
2646
|
+
componentToHex(component) {
|
|
2647
|
+
const hex = component.toString(16);
|
|
2648
|
+
return hex.length === 1 ? '0' + hex : hex;
|
|
2649
|
+
}
|
|
2650
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgtHexify, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
2651
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.11", ngImport: i0, type: NgtHexify, isStandalone: true, name: "hexify" }); }
|
|
2652
|
+
}
|
|
2653
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgtHexify, decorators: [{
|
|
2654
|
+
type: Pipe,
|
|
2655
|
+
args: [{ name: 'hexify', pure: true }]
|
|
2656
|
+
}] });
|
|
2657
|
+
|
|
2658
|
+
function omit(objFn, keysToOmit, equal = (a, b) => is.equ(a, b, { objects: 'shallow', arrays: 'shallow' })) {
|
|
2659
|
+
return computed(() => {
|
|
2660
|
+
const obj = objFn();
|
|
2661
|
+
const result = {};
|
|
2662
|
+
for (const key of Object.keys(obj)) {
|
|
2663
|
+
if (keysToOmit.includes(key))
|
|
2664
|
+
continue;
|
|
2665
|
+
Object.assign(result, { [key]: obj[key] });
|
|
2666
|
+
}
|
|
2667
|
+
return result;
|
|
2668
|
+
}, { equal });
|
|
2669
|
+
}
|
|
2670
|
+
function pick(objFn, keyOrKeys, equal) {
|
|
2671
|
+
if (Array.isArray(keyOrKeys)) {
|
|
2672
|
+
if (!equal) {
|
|
2673
|
+
equal = (a, b) => is.equ(a, b, { objects: 'shallow', arrays: 'shallow' });
|
|
2674
|
+
}
|
|
2675
|
+
return computed(() => {
|
|
2676
|
+
const obj = objFn();
|
|
2677
|
+
const result = {};
|
|
2678
|
+
for (const key of keyOrKeys) {
|
|
2679
|
+
if (!(key in obj))
|
|
2680
|
+
continue;
|
|
2681
|
+
Object.assign(result, { [key]: obj[key] });
|
|
2682
|
+
}
|
|
2683
|
+
return result;
|
|
2684
|
+
}, { equal });
|
|
2685
|
+
}
|
|
2686
|
+
return computed(() => objFn()[keyOrKeys], { equal });
|
|
2687
|
+
}
|
|
2688
|
+
function merge(objFn, toMerge, mode = 'override', equal = (a, b) => is.equ(a, b, { objects: 'shallow', arrays: 'shallow' })) {
|
|
2689
|
+
if (mode === 'override')
|
|
2690
|
+
return computed(() => ({ ...objFn(), ...toMerge }), { equal });
|
|
2691
|
+
return computed(() => ({ ...toMerge, ...objFn() }), { equal });
|
|
2692
|
+
}
|
|
2693
|
+
function createVectorComputed(vectorCtor) {
|
|
2694
|
+
return ((inputOrOptions, keyOrKeepUndefined, keepUndefined) => {
|
|
2695
|
+
if (typeof keyOrKeepUndefined === 'undefined' || typeof keyOrKeepUndefined === 'boolean') {
|
|
2696
|
+
keepUndefined = !!keyOrKeepUndefined;
|
|
2697
|
+
const input = inputOrOptions;
|
|
2698
|
+
return computed(() => {
|
|
2699
|
+
const value = input();
|
|
2700
|
+
if (keepUndefined && value == undefined)
|
|
2701
|
+
return undefined;
|
|
2702
|
+
if (typeof value === 'number')
|
|
2703
|
+
return new vectorCtor().setScalar(value);
|
|
2704
|
+
else if (Array.isArray(value))
|
|
2705
|
+
return new vectorCtor(...value);
|
|
2706
|
+
else if (value)
|
|
2707
|
+
return value;
|
|
2708
|
+
else
|
|
2709
|
+
return new vectorCtor();
|
|
2710
|
+
}, { equal: (a, b) => !!a && !!b && a.equals(b) });
|
|
2711
|
+
}
|
|
2712
|
+
const options = inputOrOptions;
|
|
2713
|
+
const key = keyOrKeepUndefined;
|
|
2714
|
+
return computed(() => {
|
|
2715
|
+
const value = options()[key];
|
|
2716
|
+
if (keepUndefined && value == undefined)
|
|
2717
|
+
return undefined;
|
|
2718
|
+
if (typeof value === 'number')
|
|
2719
|
+
return new vectorCtor().setScalar(value);
|
|
2720
|
+
else if (Array.isArray(value))
|
|
2721
|
+
return new vectorCtor(...value);
|
|
2722
|
+
else if (value)
|
|
2723
|
+
return value;
|
|
2724
|
+
else
|
|
2725
|
+
return new vectorCtor();
|
|
2726
|
+
}, { equal: (a, b) => !!a && !!b && a.equals(b) });
|
|
2727
|
+
});
|
|
2728
|
+
}
|
|
2729
|
+
const vector2 = createVectorComputed(THREE.Vector2);
|
|
2730
|
+
const vector3 = createVectorComputed(THREE.Vector3);
|
|
2731
|
+
const vector4 = createVectorComputed(THREE.Vector4);
|
|
2732
|
+
|
|
2733
|
+
class NgtPortalAutoRender {
|
|
2734
|
+
constructor() {
|
|
2735
|
+
// TODO: (chau) investigate if this is still needed
|
|
2736
|
+
// effect(() => {
|
|
2737
|
+
// this.portalStore.update((state) => ({ events: { ...state.events, priority: this.renderPriority() + 1 } }));
|
|
2738
|
+
// });
|
|
2739
|
+
this.portalStore = injectStore({ host: true });
|
|
2740
|
+
this.parentStore = injectStore({ skipSelf: true });
|
|
2741
|
+
this.portal = inject(NgtPortalImpl, { host: true });
|
|
2742
|
+
this.renderPriority = input(1, { alias: 'autoRender', transform: (value) => numberAttribute(value, 1) });
|
|
2743
|
+
effect((onCleanup) => {
|
|
2744
|
+
const portalRendered = this.portal.portalRendered();
|
|
2745
|
+
if (!portalRendered)
|
|
2746
|
+
return;
|
|
2747
|
+
// track state
|
|
2748
|
+
const [renderPriority, { internal }] = [this.renderPriority(), this.portalStore()];
|
|
2749
|
+
let oldClean;
|
|
2750
|
+
const cleanup = internal.subscribe(({ gl, scene, camera }) => {
|
|
2751
|
+
const [parentScene, parentCamera] = [
|
|
2752
|
+
this.parentStore.snapshot.scene,
|
|
2753
|
+
this.parentStore.snapshot.camera,
|
|
2754
|
+
];
|
|
2755
|
+
oldClean = gl.autoClear;
|
|
2756
|
+
if (renderPriority === 1) {
|
|
2757
|
+
// clear scene and render with default
|
|
2758
|
+
gl.autoClear = true;
|
|
2759
|
+
gl.render(parentScene, parentCamera);
|
|
2760
|
+
}
|
|
2761
|
+
// disable cleaning
|
|
2762
|
+
gl.autoClear = false;
|
|
2763
|
+
gl.clearDepth();
|
|
2764
|
+
gl.render(scene, camera);
|
|
2765
|
+
// restore
|
|
2766
|
+
gl.autoClear = oldClean;
|
|
2767
|
+
}, renderPriority, this.portalStore);
|
|
2768
|
+
onCleanup(() => cleanup());
|
|
2769
|
+
});
|
|
2770
|
+
}
|
|
2771
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgtPortalAutoRender, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2772
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.11", type: NgtPortalAutoRender, isStandalone: true, selector: "ngt-portal[autoRender]", inputs: { renderPriority: { classPropertyName: "renderPriority", publicName: "autoRender", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
|
|
2773
|
+
}
|
|
2774
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgtPortalAutoRender, decorators: [{
|
|
2775
|
+
type: Directive,
|
|
2776
|
+
args: [{ selector: 'ngt-portal[autoRender]' }]
|
|
2777
|
+
}], ctorParameters: () => [] });
|
|
2778
|
+
class NgtPortalContent {
|
|
2779
|
+
static ngTemplateContextGuard(_, ctx) {
|
|
2780
|
+
return true;
|
|
2781
|
+
}
|
|
2782
|
+
constructor() {
|
|
2783
|
+
const host = inject(ElementRef, { skipSelf: true });
|
|
2784
|
+
const { element } = inject(ViewContainerRef);
|
|
2785
|
+
const injector = inject(Injector);
|
|
2786
|
+
const commentNode = element.nativeElement;
|
|
2787
|
+
const store = injectStore();
|
|
2788
|
+
commentNode.data = NGT_PORTAL_CONTENT_FLAG;
|
|
2789
|
+
commentNode[NGT_PORTAL_CONTENT_FLAG] = store;
|
|
2790
|
+
commentNode[NGT_DOM_PARENT_FLAG] = host.nativeElement;
|
|
2791
|
+
}
|
|
2792
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgtPortalContent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
2793
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.11", type: NgtPortalContent, isStandalone: true, selector: "ng-template[portalContent]", ngImport: i0 }); }
|
|
2794
|
+
}
|
|
2795
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgtPortalContent, decorators: [{
|
|
2796
|
+
type: Directive,
|
|
2797
|
+
args: [{ selector: 'ng-template[portalContent]' }]
|
|
2798
|
+
}], ctorParameters: () => [] });
|
|
2799
|
+
function mergeState(previousRoot, store, container, pointer, raycaster, events, size) {
|
|
2800
|
+
// we never want to spread the id
|
|
2801
|
+
const { id: _, ...previousState } = previousRoot.snapshot;
|
|
2802
|
+
const state = store.snapshot;
|
|
2803
|
+
let viewport = undefined;
|
|
2804
|
+
if (state.camera && size) {
|
|
2805
|
+
const camera = state.camera;
|
|
2806
|
+
// calculate the override viewport, if present
|
|
2807
|
+
viewport = previousState.viewport.getCurrentViewport(camera, new THREE.Vector3(), size);
|
|
2808
|
+
// update the portal camera, if it differs from the previous layer
|
|
2809
|
+
if (camera !== previousState.camera)
|
|
2810
|
+
updateCamera(camera, size);
|
|
2811
|
+
}
|
|
2812
|
+
return {
|
|
2813
|
+
// the intersect consists of the previous root state
|
|
2814
|
+
...previousState,
|
|
2815
|
+
...state,
|
|
2816
|
+
// portals have their own scene, which forms the root, a raycaster and a pointer
|
|
2817
|
+
scene: container,
|
|
2818
|
+
pointer,
|
|
2819
|
+
raycaster,
|
|
2820
|
+
// their previous root is the layer before it
|
|
2821
|
+
previousRoot,
|
|
2822
|
+
events: { ...previousState.events, ...state.events, ...events },
|
|
2823
|
+
size: { ...previousState.size, ...size },
|
|
2824
|
+
viewport: { ...previousState.viewport, ...viewport },
|
|
2825
|
+
// layers are allowed to override events
|
|
2826
|
+
setEvents: (events) => store.update((state) => ({ ...state, events: { ...state.events, ...events } })),
|
|
2827
|
+
};
|
|
2828
|
+
}
|
|
2829
|
+
class NgtPortalImpl {
|
|
2830
|
+
constructor() {
|
|
2831
|
+
this.container = input.required();
|
|
2832
|
+
this.state = input({});
|
|
2833
|
+
this.contentRef = contentChild.required(NgtPortalContent, { read: TemplateRef });
|
|
2834
|
+
this.anchorRef = contentChild.required(NgtPortalContent, { read: ViewContainerRef });
|
|
2835
|
+
this.previousStore = injectStore({ skipSelf: true });
|
|
2836
|
+
this.portalStore = injectStore();
|
|
2837
|
+
this.injector = inject(Injector);
|
|
2838
|
+
this.size = pick(this.state, 'size');
|
|
2839
|
+
this.events = pick(this.state, 'events');
|
|
2840
|
+
this.restState = omit(this.state, ['size', 'events']);
|
|
2841
|
+
this.portalContentRendered = signal(false);
|
|
2842
|
+
this.portalRendered = this.portalContentRendered.asReadonly();
|
|
2843
|
+
extend({ Group });
|
|
2844
|
+
effect(() => {
|
|
2845
|
+
let [container, anchor, content] = [
|
|
2846
|
+
this.container(),
|
|
2847
|
+
this.anchorRef(),
|
|
2848
|
+
this.contentRef(),
|
|
2849
|
+
this.previousStore(),
|
|
2850
|
+
];
|
|
2851
|
+
const [size, events, restState] = [untracked(this.size), untracked(this.events), untracked(this.restState)];
|
|
2852
|
+
if (!is.instance(container)) {
|
|
2853
|
+
container = prepare(container, 'ngt-portal', { store: this.portalStore });
|
|
2854
|
+
}
|
|
2855
|
+
const instanceState = getInstanceState(container);
|
|
2856
|
+
if (instanceState && instanceState.store !== this.portalStore) {
|
|
2857
|
+
instanceState.store = this.portalStore;
|
|
2858
|
+
}
|
|
2859
|
+
this.portalStore.update(restState, mergeState(this.previousStore, this.portalStore, container, this.portalStore.snapshot.pointer, this.portalStore.snapshot.raycaster, events, size));
|
|
2860
|
+
if (this.portalViewRef) {
|
|
2861
|
+
this.portalViewRef.detectChanges();
|
|
2862
|
+
return;
|
|
2863
|
+
}
|
|
2864
|
+
this.portalViewRef = anchor.createEmbeddedView(content, { injector: this.injector }, { injector: this.injector });
|
|
2865
|
+
this.portalViewRef.detectChanges();
|
|
2866
|
+
this.portalContentRendered.set(true);
|
|
2867
|
+
});
|
|
2868
|
+
}
|
|
2869
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgtPortalImpl, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2870
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.11", 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: [
|
|
2871
|
+
{
|
|
2872
|
+
provide: NGT_STORE,
|
|
2873
|
+
useFactory: (previousStore) => {
|
|
2874
|
+
const pointer = new THREE.Vector2();
|
|
2875
|
+
const raycaster = new THREE.Raycaster();
|
|
2876
|
+
const { id: _skipId, ...previousState } = previousStore.snapshot;
|
|
2877
|
+
const store = signalState({
|
|
2878
|
+
id: makeId(),
|
|
2879
|
+
...previousState,
|
|
2880
|
+
scene: null,
|
|
2881
|
+
previousRoot: previousStore,
|
|
2882
|
+
pointer,
|
|
2883
|
+
raycaster,
|
|
2884
|
+
});
|
|
2885
|
+
store.update(mergeState(previousStore, store, null, pointer, raycaster));
|
|
2886
|
+
return store;
|
|
2887
|
+
},
|
|
2888
|
+
deps: [[new SkipSelf(), NGT_STORE]],
|
|
2889
|
+
},
|
|
2890
|
+
], queries: [{ propertyName: "contentRef", first: true, predicate: NgtPortalContent, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "anchorRef", first: true, predicate: NgtPortalContent, descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, template: `
|
|
2891
|
+
@if (portalRendered()) {
|
|
2892
|
+
<!-- Without an element that receives pointer events state.pointer will always be 0/0 -->
|
|
2893
|
+
<ngt-group (pointerover)="(undefined)" attach="none" />
|
|
2894
|
+
}
|
|
2895
|
+
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2896
|
+
}
|
|
2897
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: NgtPortalImpl, decorators: [{
|
|
2898
|
+
type: Component,
|
|
2899
|
+
args: [{
|
|
2900
|
+
selector: 'ngt-portal',
|
|
2901
|
+
template: `
|
|
2902
|
+
@if (portalRendered()) {
|
|
2903
|
+
<!-- Without an element that receives pointer events state.pointer will always be 0/0 -->
|
|
2904
|
+
<ngt-group (pointerover)="(undefined)" attach="none" />
|
|
2905
|
+
}
|
|
2906
|
+
`,
|
|
2907
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
2908
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
2909
|
+
providers: [
|
|
2910
|
+
{
|
|
2911
|
+
provide: NGT_STORE,
|
|
2912
|
+
useFactory: (previousStore) => {
|
|
2913
|
+
const pointer = new THREE.Vector2();
|
|
2914
|
+
const raycaster = new THREE.Raycaster();
|
|
2915
|
+
const { id: _skipId, ...previousState } = previousStore.snapshot;
|
|
2916
|
+
const store = signalState({
|
|
2917
|
+
id: makeId(),
|
|
2918
|
+
...previousState,
|
|
2919
|
+
scene: null,
|
|
2920
|
+
previousRoot: previousStore,
|
|
2921
|
+
pointer,
|
|
2922
|
+
raycaster,
|
|
2923
|
+
});
|
|
2924
|
+
store.update(mergeState(previousStore, store, null, pointer, raycaster));
|
|
2925
|
+
return store;
|
|
2926
|
+
},
|
|
2927
|
+
deps: [[new SkipSelf(), NGT_STORE]],
|
|
2928
|
+
},
|
|
2929
|
+
],
|
|
2930
|
+
}]
|
|
2931
|
+
}], ctorParameters: () => [] });
|
|
2932
|
+
const NgtPortal = [NgtPortalImpl, NgtPortalContent];
|
|
2933
|
+
|
|
2885
2934
|
const shallowLoose = { objects: 'shallow', strict: false };
|
|
2886
2935
|
function canvasRootInitializer(injector) {
|
|
2887
2936
|
return assertInjector(canvasRootInitializer, injector, () => {
|