webgi-gltf-extensions
Version:
WEBGI GLTF Extensions for glTF-Transform and three.js
1,013 lines (829 loc) • 100 kB
JavaScript
/**
* @license
* webgi-gltf-extensions v0.1.0
* Copyright 2022-2023 repalash <palash@shaders.app>
* MIT License
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["webgi-gltf-extensions"] = {}));
})(this, (function (exports) { 'use strict';
let EventDispatcher$1 = class EventDispatcher {
constructor() {
this._listeners = {};
}
addEventListener(type, listener) {
const listeners = this._listeners;
if (listeners[type] === undefined) {
listeners[type] = [];
}
if (listeners[type].indexOf(listener) === -1) {
listeners[type].push(listener);
}
return this;
}
removeEventListener(type, listener) {
if (this._listeners === undefined) return this;
const listeners = this._listeners;
const listenerArray = listeners[type];
if (listenerArray !== undefined) {
const index = listenerArray.indexOf(listener);
if (index !== -1) {
listenerArray.splice(index, 1);
}
}
return this;
}
dispatchEvent(event) {
if (this._listeners === undefined) return this;
const listeners = this._listeners;
const listenerArray = listeners[event.type];
if (listenerArray !== undefined) {
// Make a copy, in case listeners are removed while iterating.
const array = listenerArray.slice(0);
for (let i = 0, l = array.length; i < l; i++) {
array[i].call(this, event);
}
}
return this;
}
dispose() {
for (const key in this._listeners) {
delete this._listeners[key];
}
}
};
/**
* Represents a connection between two {@link GraphNode} resources in a {@link Graph}.
*
* The left node is considered the owner, and the right node the resource. The
* owner is responsible for being able find and remove a reference to a resource, given
* that link. The resource does not hold a reference to the link or to the owner,
* although that reverse lookup can be done on the graph.
*
* @category Graph
*/
let GraphEdge$1 = class GraphEdge extends EventDispatcher$1 {
constructor(_name, _parent, _child, _attributes = {}) {
super();
this._name = void 0;
this._parent = void 0;
this._child = void 0;
this._attributes = void 0;
this._disposed = false;
this._name = _name;
this._parent = _parent;
this._child = _child;
this._attributes = _attributes;
if (!_parent.isOnGraph(_child)) {
throw new Error('Cannot connect disconnected graphs.');
}
}
/** Name. */
getName() {
return this._name;
}
/** Owner node. */
getParent() {
return this._parent;
}
/** Resource node. */
getChild() {
return this._child;
}
/**
* Sets the child node.
*
* @internal Only {@link Graph} implementations may safely call this method directly. Use
* {@link Property.swap} or {@link Graph.swapChild} instead.
*/
setChild(child) {
this._child = child;
return this;
}
/** Attributes of the graph node relationship. */
getAttributes() {
return this._attributes;
}
/** Destroys a (currently intact) edge, updating both the graph and the owner. */
dispose() {
if (this._disposed) return;
this._disposed = true;
this.dispatchEvent({
type: 'dispose',
target: this
});
super.dispose();
}
/** Whether this link has been destroyed. */
isDisposed() {
return this._disposed;
}
};
function _extends$1() {
_extends$1 = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends$1.apply(this, arguments);
}
function isRef$1(value) {
return value instanceof GraphEdge$1;
}
function isRefList$1(value) {
return Array.isArray(value) && value[0] instanceof GraphEdge$1;
}
function isRefMap$1(value) {
return !!(isPlainObject$1(value) && getFirstValue$1(value) instanceof GraphEdge$1);
}
function getFirstValue$1(value) {
for (const key in value) {
return value[key];
}
}
function isPlainObject$1(value) {
return Boolean(value) && Object.getPrototypeOf(value) === Object.prototype;
}
const $attributes$1 = Symbol('attributes');
const $immutableKeys$1 = Symbol('immutableKeys');
/**
* Represents a node in a {@link Graph}.
*
* @category Graph
*/
let GraphNode$1 = class GraphNode extends EventDispatcher$1 {
/**
* Internal graph used to search and maintain references.
* @hidden
*/
/**
* Attributes (literal values and GraphNode references) associated with this instance. For each
* GraphNode reference, the attributes stores a {@link GraphEdge}. List and Map references are
* stored as arrays and dictionaries of edges.
* @internal
*/
/**
* Attributes included with `getDefaultAttributes` are considered immutable, and cannot be
* modifed by `.setRef()`, `.copy()`, or other GraphNode methods. Both the edges and the
* properties will be disposed with the parent GraphNode.
*
* Currently, only single-edge references (getRef/setRef) are supported as immutables.
*
* @internal
*/
constructor(graph) {
super();
this._disposed = false;
this.graph = void 0;
this[$attributes$1] = void 0;
this[$immutableKeys$1] = void 0;
this.graph = graph;
this[$immutableKeys$1] = new Set();
this[$attributes$1] = this._createAttributes();
}
/**
* Returns default attributes for the graph node. Subclasses having any attributes (either
* literal values or references to other graph nodes) must override this method. Literal
* attributes should be given their default values, if any. References should generally be
* initialized as empty (Ref → null, RefList → [], RefMap → {}) and then modified by setters.
*
* Any single-edge references (setRef) returned by this method will be considered immutable,
* to be owned by and disposed with the parent node. Multi-edge references (addRef, removeRef,
* setRefMap) cannot be returned as default attributes.
*/
getDefaults() {
return {};
}
/**
* Constructs and returns an object used to store a graph nodes attributes. Compared to the
* default Attributes interface, this has two distinctions:
*
* 1. Slots for GraphNode<T> objects are replaced with slots for GraphEdge<this, GraphNode<T>>
* 2. GraphNode<T> objects provided as defaults are considered immutable
*
* @internal
*/
_createAttributes() {
const defaultAttributes = this.getDefaults();
const attributes = {};
for (const key in defaultAttributes) {
const value = defaultAttributes[key];
if (value instanceof GraphNode) {
const ref = this.graph.createEdge(key, this, value);
ref.addEventListener('dispose', () => value.dispose());
this[$immutableKeys$1].add(key);
attributes[key] = ref;
} else {
attributes[key] = value;
}
}
return attributes;
}
/** @internal Returns true if two nodes are on the same {@link Graph}. */
isOnGraph(other) {
return this.graph === other.graph;
}
/** Returns true if the node has been permanently removed from the graph. */
isDisposed() {
return this._disposed;
}
/**
* Removes both inbound references to and outbound references from this object. At the end
* of the process the object holds no references, and nothing holds references to it. A
* disposed object is not reusable.
*/
dispose() {
if (this._disposed) return;
this.graph.listChildEdges(this).forEach(edge => edge.dispose());
this.graph.disconnectParents(this);
this._disposed = true;
this.dispatchEvent({
type: 'dispose'
});
}
/**
* Removes all inbound references to this object. At the end of the process the object is
* considered 'detached': it may hold references to child resources, but nothing holds
* references to it. A detached object may be re-attached.
*/
detach() {
this.graph.disconnectParents(this);
return this;
}
/**
* Transfers this object's references from the old node to the new one. The old node is fully
* detached from this parent at the end of the process.
*
* @hidden
*/
swap(old, replacement) {
for (const attribute in this[$attributes$1]) {
const value = this[$attributes$1][attribute];
if (isRef$1(value)) {
const ref = value;
if (ref.getChild() === old) {
this.setRef(attribute, replacement, ref.getAttributes());
}
} else if (isRefList$1(value)) {
const refs = value;
const ref = refs.find(ref => ref.getChild() === old);
if (ref) {
const refAttributes = ref.getAttributes();
this.removeRef(attribute, old).addRef(attribute, replacement, refAttributes);
}
} else if (isRefMap$1(value)) {
const refMap = value;
for (const key in refMap) {
const ref = refMap[key];
if (ref.getChild() === old) {
this.setRefMap(attribute, key, replacement, ref.getAttributes());
}
}
}
}
return this;
}
/**********************************************************************************************
* Literal attributes.
*/
/** @hidden */
get(attribute) {
return this[$attributes$1][attribute];
}
/** @hidden */
set(attribute, value) {
this[$attributes$1][attribute] = value;
return this.dispatchEvent({
type: 'change',
attribute
});
}
/**********************************************************************************************
* Ref: 1:1 graph node references.
*/
/** @hidden */
getRef(attribute) {
const ref = this[$attributes$1][attribute];
return ref ? ref.getChild() : null;
}
/** @hidden */
setRef(attribute, value, attributes) {
if (this[$immutableKeys$1].has(attribute)) {
throw new Error(`Cannot overwrite immutable attribute, "${attribute}".`);
}
const prevRef = this[$attributes$1][attribute];
if (prevRef) prevRef.dispose(); // TODO(cleanup): Possible duplicate event.
if (!value) return this;
const ref = this.graph.createEdge(attribute, this, value, attributes);
ref.addEventListener('dispose', () => {
delete this[$attributes$1][attribute];
this.dispatchEvent({
type: 'change',
attribute
});
});
this[$attributes$1][attribute] = ref;
return this.dispatchEvent({
type: 'change',
attribute
});
}
/**********************************************************************************************
* RefList: 1:many graph node references.
*/
/** @hidden */
listRefs(attribute) {
const refs = this[$attributes$1][attribute];
return refs.map(ref => ref.getChild());
}
/** @hidden */
addRef(attribute, value, attributes) {
const ref = this.graph.createEdge(attribute, this, value, attributes);
const refs = this[$attributes$1][attribute];
refs.push(ref);
ref.addEventListener('dispose', () => {
const retained = refs.filter(l => l !== ref);
refs.length = 0;
for (const retainedRef of retained) refs.push(retainedRef);
this.dispatchEvent({
type: 'change',
attribute
});
});
return this.dispatchEvent({
type: 'change',
attribute
});
}
/** @hidden */
removeRef(attribute, value) {
const refs = this[$attributes$1][attribute];
const pruned = refs.filter(ref => ref.getChild() === value);
pruned.forEach(ref => ref.dispose()); // TODO(cleanup): Possible duplicate event.
return this;
}
/**********************************************************************************************
* RefMap: Named 1:many (map) graph node references.
*/
/** @hidden */
listRefMapKeys(key) {
return Object.keys(this[$attributes$1][key]);
}
/** @hidden */
listRefMapValues(key) {
return Object.values(this[$attributes$1][key]).map(ref => ref.getChild());
}
/** @hidden */
getRefMap(attribute, key) {
const refMap = this[$attributes$1][attribute];
return refMap[key] ? refMap[key].getChild() : null;
}
/** @hidden */
setRefMap(attribute, key, value, metadata) {
const refMap = this[$attributes$1][attribute];
const prevRef = refMap[key];
if (prevRef) prevRef.dispose(); // TODO(cleanup): Possible duplicate event.
if (!value) return this;
metadata = Object.assign(metadata || {}, {
key: key
});
const ref = this.graph.createEdge(attribute, this, value, _extends$1({}, metadata, {
key
}));
ref.addEventListener('dispose', () => {
delete refMap[key];
this.dispatchEvent({
type: 'change',
attribute,
key
});
});
refMap[key] = ref;
return this.dispatchEvent({
type: 'change',
attribute,
key
});
}
/**********************************************************************************************
* Events.
*/
dispatchEvent(event) {
super.dispatchEvent(_extends$1({}, event, {
target: this
}));
this.graph.dispatchEvent(_extends$1({}, event, {
target: this,
type: `node:${event.type}`
}));
return this;
}
};
var h$1,f$1,l$1,d$1,g$1;!function(t){t.ACCESSOR="Accessor",t.ANIMATION="Animation",t.ANIMATION_CHANNEL="AnimationChannel",t.ANIMATION_SAMPLER="AnimationSampler",t.BUFFER="Buffer",t.CAMERA="Camera",t.MATERIAL="Material",t.MESH="Mesh",t.PRIMITIVE="Primitive",t.PRIMITIVE_TARGET="PrimitiveTarget",t.NODE="Node",t.ROOT="Root",t.SCENE="Scene",t.SKIN="Skin",t.TEXTURE="Texture",t.TEXTURE_INFO="TextureInfo";}(h$1||(h$1={})),function(t){t.INTERLEAVED="interleaved",t.SEPARATE="separate";}(f$1||(f$1={})),function(t){t.ARRAY_BUFFER="ARRAY_BUFFER",t.ELEMENT_ARRAY_BUFFER="ELEMENT_ARRAY_BUFFER",t.INVERSE_BIND_MATRICES="INVERSE_BIND_MATRICES",t.OTHER="OTHER",t.SPARSE="SPARSE";}(l$1||(l$1={})),function(t){t[t.R=4096]="R",t[t.G=256]="G",t[t.B=16]="B",t[t.A=1]="A";}(d$1||(d$1={})),function(t){t.GLTF="GLTF",t.GLB="GLB";}(g$1||(g$1={}));var m$1,w$1="undefined"!=typeof Float32Array?Float32Array:Array;function y$1(t){return Math.hypot(t[0],t[1],t[2])}Math.hypot||(Math.hypot=function(){for(var t=0,e=arguments.length;e--;)t+=arguments[e]*arguments[e];return Math.sqrt(t)}),m$1=new w$1(3),w$1!=Float32Array&&(m$1[0]=0,m$1[1]=0,m$1[2]=0);let M$1 = class M{static hexToFactor(t,e){t=Math.floor(t);const r=e;return r[0]=(t>>16&255)/255,r[1]=(t>>8&255)/255,r[2]=(255&t)/255,this.convertSRGBToLinear(e,e)}static factorToHex(t){const e=[...t],[r,s,n]=this.convertLinearToSRGB(t,e);return 255*r<<16^255*s<<8^255*n<<0}static convertSRGBToLinear(t,e){const r=t,s=e;for(let t=0;t<3;t++)s[t]=r[t]<.04045?.0773993808*r[t]:Math.pow(.9478672986*r[t]+.0521327014,2.4);return e}static convertLinearToSRGB(t,e){const r=t,s=e;for(let t=0;t<3;t++)s[t]=r[t]<.0031308?12.92*r[t]:1.055*Math.pow(r[t],.41666)-.055;return e}};function O$1(t){return "[object Object]"===Object.prototype.toString.call(t)}function C$1(t){if(!1===O$1(t))return !1;const e=t.constructor;if(void 0===e)return !0;const r=e.prototype;return !1!==O$1(r)&&!1!==Object.prototype.hasOwnProperty.call(r,"isPrototypeOf")}var F$1;!function(t){t[t.SILENT=4]="SILENT",t[t.ERROR=3]="ERROR",t[t.WARN=2]="WARN",t[t.INFO=1]="INFO",t[t.DEBUG=0]="DEBUG";}(F$1||(F$1={}));let U$1 = class U{constructor(t){this.verbosity=void 0,this.verbosity=t;}debug(t){this.verbosity<=U.Verbosity.DEBUG&&console.debug(t);}info(t){this.verbosity<=U.Verbosity.INFO&&console.info(t);}warn(t){this.verbosity<=U.Verbosity.WARN&&console.warn(t);}error(t){this.verbosity<=U.Verbosity.ERROR&&console.error(t);}};U$1.Verbosity=F$1,U$1.DEFAULT_INSTANCE=new U$1(U$1.Verbosity.INFO);let P$1 = class P{static identity(t){return t}static eq(t,e,r=1e-5){if(t.length!==e.length)return !1;for(let s=0;s<t.length;s++)if(Math.abs(t[s]-e[s])>r)return !1;return !0}static decodeNormalizedInt(t,e){switch(e){case 5126:return t;case 5123:return t/65535;case 5121:return t/255;case 5122:return Math.max(t/32767,-1);case 5120:return Math.max(t/127,-1);default:throw new Error("Invalid component type.")}}static denormalize(t,e){return P.decodeNormalizedInt(t,e)}static encodeNormalizedInt(t,e){switch(e){case 5126:return t;case 5123:return Math.round(65535*t);case 5121:return Math.round(255*t);case 5122:return Math.round(32767*t);case 5120:return Math.round(127*t);default:throw new Error("Invalid component type.")}}static normalize(t,e){return P.encodeNormalizedInt(t,e)}static decompose(t,e,r,s){let n=y$1([t[0],t[1],t[2]]);const i=y$1([t[4],t[5],t[6]]),o=y$1([t[8],t[9],t[10]]);var a,u,c,h,f,l,d,g,p,m,v,T,b,x,A,E,M;((u=(a=t)[0])*(d=a[5])-(c=a[1])*(l=a[4]))*((T=a[10])*(M=a[15])-(b=a[11])*(E=a[14]))-(u*(g=a[6])-(h=a[2])*l)*((v=a[9])*M-b*(A=a[13]))+(u*(p=a[7])-(f=a[3])*l)*(v*E-T*A)+(c*g-h*d)*((m=a[8])*M-b*(x=a[12]))-(c*p-f*d)*(m*E-T*x)+(h*p-f*g)*(m*A-v*x)<0&&(n=-n),e[0]=t[12],e[1]=t[13],e[2]=t[14];const S=t.slice(),I=1/n,R=1/i,N=1/o;S[0]*=I,S[1]*=I,S[2]*=I,S[4]*=R,S[5]*=R,S[6]*=R,S[8]*=N,S[9]*=N,S[10]*=N,function(t,e){var r=new w$1(3);!function(t,e){var r=e[4],s=e[5],n=e[6],i=e[8],o=e[9],a=e[10];t[0]=Math.hypot(e[0],e[1],e[2]),t[1]=Math.hypot(r,s,n),t[2]=Math.hypot(i,o,a);}(r,e);var s=1/r[0],n=1/r[1],i=1/r[2],o=e[0]*s,a=e[1]*n,u=e[2]*i,c=e[4]*s,h=e[5]*n,f=e[6]*i,l=e[8]*s,d=e[9]*n,g=e[10]*i,p=o+h+g,m=0;p>0?(m=2*Math.sqrt(p+1),t[3]=.25*m,t[0]=(f-d)/m,t[1]=(l-u)/m,t[2]=(a-c)/m):o>h&&o>g?(m=2*Math.sqrt(1+o-h-g),t[3]=(f-d)/m,t[0]=.25*m,t[1]=(a+c)/m,t[2]=(l+u)/m):h>g?(m=2*Math.sqrt(1+h-o-g),t[3]=(l-u)/m,t[0]=(a+c)/m,t[1]=.25*m,t[2]=(f+d)/m):(m=2*Math.sqrt(1+g-o-h),t[3]=(a-c)/m,t[0]=(l+u)/m,t[1]=(f+d)/m,t[2]=.25*m);}(r,S),s[0]=n,s[1]=i,s[2]=o;}static compose(t,e,r,s){const n=s,i=e[0],o=e[1],a=e[2],u=e[3],c=i+i,h=o+o,f=a+a,l=i*c,d=i*h,g=i*f,p=o*h,m=o*f,w=a*f,y=u*c,v=u*h,T=u*f,b=r[0],x=r[1],A=r[2];return n[0]=(1-(p+w))*b,n[1]=(d+T)*b,n[2]=(g-v)*b,n[3]=0,n[4]=(d-T)*x,n[5]=(1-(l+w))*x,n[6]=(m+y)*x,n[7]=0,n[8]=(g+v)*A,n[9]=(m-y)*A,n[10]=(1-(l+p))*A,n[11]=0,n[12]=t[0],n[13]=t[1],n[14]=t[2],n[15]=1,n}};function L$1(t,e){if(!!t!=!!e)return !1;const r=t.getChild(),s=e.getChild();return r===s||r.equals(s)}function j$1(t,e){if(!!t!=!!e)return !1;if(t.length!==e.length)return !1;for(let r=0;r<t.length;r++){const s=t[r],n=e[r];if(s.getChild()!==n.getChild()&&!s.getChild().equals(n.getChild()))return !1}return !0}function D$1(t,e){if(!!t!=!!e)return !1;const r=Object.keys(t),s=Object.keys(e);if(r.length!==s.length)return !1;for(const r in t){const s=t[r],n=e[r];if(!!s!=!!n)return !1;const i=s.getChild(),o=n.getChild();if(i!==o&&!i.equals(o))return !1}return !0}function _$1(t,e){if(t===e)return !0;if(!!t!=!!e||!t||!e)return !1;if(t.length!==e.length)return !1;for(let r=0;r<t.length;r++)if(t[r]!==e[r])return !1;return !0}function k$1(t,e){if(t===e)return !0;if(!!t!=!!e)return !1;if(!C$1(t)||!C$1(e))return t===e;const r=t,s=e;let n,i=0,o=0;for(n in r)i++;for(n in s)o++;if(i!==o)return !1;for(n in r){const t=r[n],e=s[n];if(z$1(t)&&z$1(e)){if(!_$1(t,e))return !1}else if(C$1(t)&&C$1(e)){if(!k$1(t,e))return !1}else if(t!==e)return !1}return !0}function z$1(t){return Array.isArray(t)||ArrayBuffer.isView(t)}const q$1=t=>t,H$1=new Set;let Y$1 = class Y extends GraphNode$1{constructor(t,r=""){super(t),this[$attributes$1].name=r,this.init(),this.dispatchEvent({type:"create"});}getGraph(){return this.graph}getDefaults(){return Object.assign(super.getDefaults(),{name:"",extras:{}})}set(t,e){return Array.isArray(e)&&(e=e.slice()),super.set(t,e)}getName(){return this.get("name")}setName(t){return this.set("name",t)}getExtras(){return this.get("extras")}setExtras(t){return this.set("extras",t)}clone(){return new(this.constructor)(this.graph).copy(this,q$1)}copy(t,o=q$1){for(const t in this[$attributes$1]){const o=this[$attributes$1][t];if(o instanceof GraphEdge$1)this[$immutableKeys$1].has(t)||o.dispose();else if(isRefList$1(o))for(const t of o)t.dispose();else if(isRefMap$1(o))for(const t in o)o[t].dispose();}for(const a in t[$attributes$1]){const u=this[$attributes$1][a],c=t[$attributes$1][a];if(c instanceof GraphEdge$1)this[$immutableKeys$1].has(a)?u.getChild().copy(o(c.getChild()),o):this.setRef(a,o(c.getChild()),c.getAttributes());else if(isRefList$1(c))for(const t of c)this.addRef(a,o(t.getChild()),t.getAttributes());else if(isRefMap$1(c))for(const t in c){const e=c[t];this.setRefMap(a,t,o(e.getChild()),e.getAttributes());}else this[$attributes$1][a]=C$1(c)?JSON.parse(JSON.stringify(c)):Array.isArray(c)||c instanceof ArrayBuffer||ArrayBuffer.isView(c)?c.slice():c;}return this}equals(t,r=H$1){if(this===t)return !0;if(this.propertyType!==t.propertyType)return !1;for(const s in this[$attributes$1]){if(r.has(s))continue;const a=this[$attributes$1][s],u=t[$attributes$1][s];if(isRef$1(a)||isRef$1(u)){if(!L$1(a,u))return !1}else if(isRefList$1(a)||isRefList$1(u)){if(!j$1(a,u))return !1}else if(isRefMap$1(a)||isRefMap$1(u)){if(!D$1(a,u))return !1}else if(C$1(a)||C$1(u)){if(!k$1(a,u))return !1}else if(z$1(a)||z$1(u)){if(!_$1(a,u))return !1}else if(a!==u)return !1}return !0}detach(){return this.graph.disconnectParents(this,t=>"Root"!==t.propertyType),this}listParents(){return this.graph.listParents(this)}};let Z$1 = class Z extends Y$1{getDefaults(){return Object.assign(super.getDefaults(),{extensions:{}})}getExtension(t){return this.getRefMap("extensions",t)}setExtension(t,e){return e&&e.t(this),this.setRefMap("extensions",t,e)}listExtensions(){return this.listRefMapValues("extensions")}};let K$1 = class K extends Z$1{constructor(...t){super(...t),this.i=P$1.identity,this.o=P$1.identity;}init(){this.propertyType=h$1.ACCESSOR;}getDefaults(){return Object.assign(super.getDefaults(),{array:null,type:K.Type.SCALAR,componentType:K.ComponentType.FLOAT,normalized:!1,sparse:!1,buffer:null})}copy(t,e=q$1){return super.copy(t,e),this.i=t.i,this.o=t.o,this}static getElementSize(t){switch(t){case K.Type.SCALAR:return 1;case K.Type.VEC2:return 2;case K.Type.VEC3:return 3;case K.Type.VEC4:case K.Type.MAT2:return 4;case K.Type.MAT3:return 9;case K.Type.MAT4:return 16;default:throw new Error("Unexpected type: "+t)}}static getComponentSize(t){switch(t){case K.ComponentType.BYTE:case K.ComponentType.UNSIGNED_BYTE:return 1;case K.ComponentType.SHORT:case K.ComponentType.UNSIGNED_SHORT:return 2;case K.ComponentType.UNSIGNED_INT:case K.ComponentType.FLOAT:return 4;default:throw new Error("Unexpected component type: "+t)}}getMinNormalized(t){const e=this.getElementSize();this.getMin(t);for(let r=0;r<e;r++)t[r]=this.o(t[r]);return t}getMin(t){const e=this.get("array"),r=this.getCount(),s=this.getElementSize();for(let e=0;e<s;e++)t[e]=Infinity;for(let n=0;n<r*s;n+=s)for(let r=0;r<s;r++){const s=e[n+r];Number.isFinite(s)&&(t[r]=Math.min(t[r],s));}return t}getMaxNormalized(t){const e=this.getElementSize();this.getMax(t);for(let r=0;r<e;r++)t[r]=this.o(t[r]);return t}getMax(t){const e=this.get("array"),r=this.getCount(),s=this.getElementSize();for(let e=0;e<s;e++)t[e]=-Infinity;for(let n=0;n<r*s;n+=s)for(let r=0;r<s;r++){const s=e[n+r];Number.isFinite(s)&&(t[r]=Math.max(t[r],s));}return t}getCount(){const t=this.get("array");return t?t.length/this.getElementSize():0}getType(){return this.get("type")}setType(t){return this.set("type",t)}getElementSize(){return K.getElementSize(this.get("type"))}getComponentSize(){return this.get("array").BYTES_PER_ELEMENT}getComponentType(){return this.get("componentType")}getNormalized(){return this.get("normalized")}setNormalized(t){return this.set("normalized",t),t?(this.o=t=>P$1.decodeNormalizedInt(t,this.get("componentType")),this.i=t=>P$1.encodeNormalizedInt(t,this.get("componentType"))):(this.o=P$1.identity,this.i=P$1.identity),this}getScalar(t){const e=this.getElementSize();return this.o(this.get("array")[t*e])}setScalar(t,e){return this.get("array")[t*this.getElementSize()]=this.i(e),this}getElement(t,e){const r=this.getElementSize(),s=this.get("array");for(let n=0;n<r;n++)e[n]=this.o(s[t*r+n]);return e}setElement(t,e){const r=this.getElementSize(),s=this.get("array");for(let n=0;n<r;n++)s[t*r+n]=this.i(e[n]);return this}getSparse(){return this.get("sparse")}setSparse(t){return this.set("sparse",t)}getBuffer(){return this.getRef("buffer")}setBuffer(t){return this.setRef("buffer",t)}getArray(){return this.get("array")}setArray(t){return this.set("componentType",t?function(t){switch(t.constructor){case Float32Array:return K.ComponentType.FLOAT;case Uint32Array:return K.ComponentType.UNSIGNED_INT;case Uint16Array:return K.ComponentType.UNSIGNED_SHORT;case Uint8Array:return K.ComponentType.UNSIGNED_BYTE;case Int16Array:return K.ComponentType.SHORT;case Int8Array:return K.ComponentType.BYTE;default:throw new Error("Unknown accessor componentType.")}}(t):K.ComponentType.FLOAT),this.set("array",t),this}getByteLength(){const t=this.get("array");return t?t.byteLength:0}};K$1.Type={SCALAR:"SCALAR",VEC2:"VEC2",VEC3:"VEC3",VEC4:"VEC4",MAT2:"MAT2",MAT3:"MAT3",MAT4:"MAT4"},K$1.ComponentType={BYTE:5120,UNSIGNED_BYTE:5121,SHORT:5122,UNSIGNED_SHORT:5123,UNSIGNED_INT:5125,FLOAT:5126};let X$1 = class X extends Z$1{init(){this.propertyType=h$1.ANIMATION_CHANNEL;}getDefaults(){return Object.assign(super.getDefaults(),{targetPath:null,targetNode:null,sampler:null})}getTargetPath(){return this.get("targetPath")}setTargetPath(t){return this.set("targetPath",t)}getTargetNode(){return this.getRef("targetNode")}setTargetNode(t){return this.setRef("targetNode",t)}getSampler(){return this.getRef("sampler")}setSampler(t){return this.setRef("sampler",t)}};X$1.TargetPath={TRANSLATION:"translation",ROTATION:"rotation",SCALE:"scale",WEIGHTS:"weights"};let tt$1 = class tt extends Z$1{init(){this.propertyType=h$1.ANIMATION_SAMPLER;}getDefaultAttributes(){return Object.assign(super.getDefaults(),{interpolation:tt.Interpolation.LINEAR,input:null,output:null})}getInterpolation(){return this.get("interpolation")}setInterpolation(t){return this.set("interpolation",t)}getInput(){return this.getRef("input")}setInput(t){return this.setRef("input",t,{usage:l$1.OTHER})}getOutput(){return this.getRef("output")}setOutput(t){return this.setRef("output",t,{usage:l$1.OTHER})}};tt$1.Interpolation={LINEAR:"LINEAR",STEP:"STEP",CUBICSPLINE:"CUBICSPLINE"};let rt$1 = class rt extends Z$1{init(){this.propertyType=h$1.CAMERA;}getDefaults(){return Object.assign(super.getDefaults(),{type:rt.Type.PERSPECTIVE,znear:.1,zfar:100,aspectRatio:null,yfov:2*Math.PI*50/360,xmag:1,ymag:1})}getType(){return this.get("type")}setType(t){return this.set("type",t)}getZNear(){return this.get("znear")}setZNear(t){return this.set("znear",t)}getZFar(){return this.get("zfar")}setZFar(t){return this.set("zfar",t)}getAspectRatio(){return this.get("aspectRatio")}setAspectRatio(t){return this.set("aspectRatio",t)}getYFov(){return this.get("yfov")}setYFov(t){return this.set("yfov",t)}getXMag(){return this.get("xmag")}setXMag(t){return this.set("xmag",t)}getYMag(){return this.get("ymag")}setYMag(t){return this.set("ymag",t)}};rt$1.Type={PERSPECTIVE:"perspective",ORTHOGRAPHIC:"orthographic"};let st$1 = class st extends Y$1{t(t){if(!this.parentTypes.includes(t.propertyType))throw new Error(`Parent "${t.propertyType}" invalid for child "${this.propertyType}".`)}};st$1.EXTENSION_NAME=void 0;let nt$1 = class nt extends Z$1{init(){this.propertyType=h$1.TEXTURE_INFO;}getDefaults(){return Object.assign(super.getDefaults(),{texCoord:0,magFilter:null,minFilter:null,wrapS:nt.WrapMode.REPEAT,wrapT:nt.WrapMode.REPEAT})}getTexCoord(){return this.get("texCoord")}setTexCoord(t){return this.set("texCoord",t)}getMagFilter(){return this.get("magFilter")}setMagFilter(t){return this.set("magFilter",t)}getMinFilter(){return this.get("minFilter")}setMinFilter(t){return this.set("minFilter",t)}getWrapS(){return this.get("wrapS")}setWrapS(t){return this.set("wrapS",t)}getWrapT(){return this.get("wrapT")}setWrapT(t){return this.set("wrapT",t)}};nt$1.WrapMode={CLAMP_TO_EDGE:33071,MIRRORED_REPEAT:33648,REPEAT:10497},nt$1.MagFilter={NEAREST:9728,LINEAR:9729},nt$1.MinFilter={NEAREST:9728,LINEAR:9729,NEAREST_MIPMAP_NEAREST:9984,LINEAR_MIPMAP_NEAREST:9985,NEAREST_MIPMAP_LINEAR:9986,LINEAR_MIPMAP_LINEAR:9987};const{R:it$1,G:ot$1,B:at$1,A:ut$1}=d$1;let ct$1 = class ct extends Z$1{init(){this.propertyType=h$1.MATERIAL;}getDefaults(){return Object.assign(super.getDefaults(),{alphaMode:ct.AlphaMode.OPAQUE,alphaCutoff:.5,doubleSided:!1,baseColorFactor:[1,1,1,1],baseColorTexture:null,baseColorTextureInfo:new nt$1(this.graph,"baseColorTextureInfo"),emissiveFactor:[0,0,0],emissiveTexture:null,emissiveTextureInfo:new nt$1(this.graph,"emissiveTextureInfo"),normalScale:1,normalTexture:null,normalTextureInfo:new nt$1(this.graph,"normalTextureInfo"),occlusionStrength:1,occlusionTexture:null,occlusionTextureInfo:new nt$1(this.graph,"occlusionTextureInfo"),roughnessFactor:1,metallicFactor:1,metallicRoughnessTexture:null,metallicRoughnessTextureInfo:new nt$1(this.graph,"metallicRoughnessTextureInfo")})}getDoubleSided(){return this.get("doubleSided")}setDoubleSided(t){return this.set("doubleSided",t)}getAlpha(){return this.get("baseColorFactor")[3]}setAlpha(t){const e=this.get("baseColorFactor").slice();return e[3]=t,this.set("baseColorFactor",e)}getAlphaMode(){return this.get("alphaMode")}setAlphaMode(t){return this.set("alphaMode",t)}getAlphaCutoff(){return this.get("alphaCutoff")}setAlphaCutoff(t){return this.set("alphaCutoff",t)}getBaseColorFactor(){return this.get("baseColorFactor")}setBaseColorFactor(t){return this.set("baseColorFactor",t)}getBaseColorHex(){return M$1.factorToHex(this.get("baseColorFactor"))}setBaseColorHex(t){const e=this.get("baseColorFactor").slice();return this.set("baseColorFactor",M$1.hexToFactor(t,e))}getBaseColorTexture(){return this.getRef("baseColorTexture")}getBaseColorTextureInfo(){return this.getRef("baseColorTexture")?this.getRef("baseColorTextureInfo"):null}setBaseColorTexture(t){return this.setRef("baseColorTexture",t,{channels:it$1|ot$1|at$1|ut$1,isColor:!0})}getEmissiveFactor(){return this.get("emissiveFactor")}setEmissiveFactor(t){return this.set("emissiveFactor",t)}getEmissiveHex(){return M$1.factorToHex(this.get("emissiveFactor"))}setEmissiveHex(t){const e=this.get("emissiveFactor").slice();return this.set("emissiveFactor",M$1.hexToFactor(t,e))}getEmissiveTexture(){return this.getRef("emissiveTexture")}getEmissiveTextureInfo(){return this.getRef("emissiveTexture")?this.getRef("emissiveTextureInfo"):null}setEmissiveTexture(t){return this.setRef("emissiveTexture",t,{channels:it$1|ot$1|at$1,isColor:!0})}getNormalScale(){return this.get("normalScale")}setNormalScale(t){return this.set("normalScale",t)}getNormalTexture(){return this.getRef("normalTexture")}getNormalTextureInfo(){return this.getRef("normalTexture")?this.getRef("normalTextureInfo"):null}setNormalTexture(t){return this.setRef("normalTexture",t,{channels:it$1|ot$1|at$1})}getOcclusionStrength(){return this.get("occlusionStrength")}setOcclusionStrength(t){return this.set("occlusionStrength",t)}getOcclusionTexture(){return this.getRef("occlusionTexture")}getOcclusionTextureInfo(){return this.getRef("occlusionTexture")?this.getRef("occlusionTextureInfo"):null}setOcclusionTexture(t){return this.setRef("occlusionTexture",t,{channels:it$1})}getRoughnessFactor(){return this.get("roughnessFactor")}setRoughnessFactor(t){return this.set("roughnessFactor",t)}getMetallicFactor(){return this.get("metallicFactor")}setMetallicFactor(t){return this.set("metallicFactor",t)}getMetallicRoughnessTexture(){return this.getRef("metallicRoughnessTexture")}getMetallicRoughnessTextureInfo(){return this.getRef("metallicRoughnessTexture")?this.getRef("metallicRoughnessTextureInfo"):null}setMetallicRoughnessTexture(t){return this.setRef("metallicRoughnessTexture",t,{channels:ot$1|at$1})}};ct$1.AlphaMode={OPAQUE:"OPAQUE",MASK:"MASK",BLEND:"BLEND"};let lt$1 = class lt extends Z$1{init(){this.propertyType=h$1.PRIMITIVE;}getDefaults(){return Object.assign(super.getDefaults(),{mode:lt.Mode.TRIANGLES,material:null,indices:null,attributes:{},targets:[]})}getIndices(){return this.getRef("indices")}setIndices(t){return this.setRef("indices",t,{usage:l$1.ELEMENT_ARRAY_BUFFER})}getAttribute(t){return this.getRefMap("attributes",t)}setAttribute(t,e){return this.setRefMap("attributes",t,e,{usage:l$1.ARRAY_BUFFER})}listAttributes(){return this.listRefMapValues("attributes")}listSemantics(){return this.listRefMapKeys("attributes")}getMaterial(){return this.getRef("material")}setMaterial(t){return this.setRef("material",t)}getMode(){return this.get("mode")}setMode(t){return this.set("mode",t)}listTargets(){return this.listRefs("targets")}addTarget(t){return this.addRef("targets",t)}removeTarget(t){return this.removeRef("targets",t)}};lt$1.Mode={POINTS:0,LINES:1,LINE_LOOP:2,LINE_STRIP:3,TRIANGLES:4,TRIANGLE_STRIP:5,TRIANGLE_FAN:6};let Tt$1 = class Tt{constructor(t){this.extensionName="",this.prereadTypes=[],this.prewriteTypes=[],this.readDependencies=[],this.writeDependencies=[],this.document=void 0,this.required=!1,this.properties=new Set,this.S=void 0,this.document=t,t.getRoot().g(this),this.S=t=>{const e=t,r=e.target;r instanceof st$1&&r.extensionName===this.extensionName&&("node:create"===e.type&&this.I(r),"node:dispose"===e.type&&this.N(r));};const e=t.getGraph();e.addEventListener("node:create",this.S),e.addEventListener("node:dispose",this.S);}dispose(){this.document.getRoot().p(this);const t=this.document.getGraph();t.removeEventListener("node:create",this.S),t.removeEventListener("node:dispose",this.S);for(const t of this.properties)t.dispose();}static register(){}isRequired(){return this.required}setRequired(t){return this.required=t,this}listProperties(){return Array.from(this.properties)}I(t){return this.properties.add(t),this}N(t){return this.properties.delete(t),this}install(t,e){return this}preread(t,e){return this}prewrite(t,e){return this}};Tt$1.EXTENSION_NAME=void 0;var Mt$1;!function(t){t[t.ARRAY_BUFFER=34962]="ARRAY_BUFFER",t[t.ELEMENT_ARRAY_BUFFER=34963]="ELEMENT_ARRAY_BUFFER";}(Mt$1||(Mt$1={}));({[l$1.ARRAY_BUFFER]:Mt$1.ARRAY_BUFFER,[l$1.ELEMENT_ARRAY_BUFFER]:Mt$1.ELEMENT_ARRAY_BUFFER});K$1.ComponentType;var Ut$1;!function(t){t[t.JSON=1313821514]="JSON",t[t.BIN=5130562]="BIN";}(Ut$1||(Ut$1={}));
const viewerGLTFExtension = 'WEBGI_viewer';
class WebGIViewerExtensionProperty extends st$1 {
constructor() {
super(...arguments);
this.extensionName = viewerGLTFExtension;
this.parentTypes = [h$1.SCENE];
this.propertyType = 'ViewerJSON';
}
// eslint-disable-next-line @typescript-eslint/naming-convention
init() { return; }
}
class WebGIViewerExtension extends Tt$1 {
constructor() {
super(...arguments);
this.extensionName = viewerGLTFExtension;
this._viewerConfig = {};
// @ts-ignore
this._texturesRef = [];
this.required = true;
}
read(context) {
this._viewerConfig = {};
context.jsonDoc.json.scenes?.forEach((sceneDef, sceneIndex) => {
if (sceneDef.extensions && sceneDef.extensions[viewerGLTFExtension]) {
const prop = new WebGIViewerExtensionProperty(this.document.getGraph());
context.scenes[sceneIndex].setExtension(viewerGLTFExtension, prop);
this._viewerConfig = sceneDef.extensions[viewerGLTFExtension];
// prop.setExtras()
/*
const buffers = [] as any[]
Object.values(viewerConfig.resources).forEach((res: any) => {
Object.values(res).forEach((item: any) => {
if (!item.url) return
if (item.url.data?.image !== null) {
buffers.push(item.url)
}
})
})
const jsonDoc = context.jsonDoc
console.log(buffers)
for (const buffer of buffers) {
const img = buffer.data.image as number
const imageDef = jsonDoc.json.images![img]
const bufferViewDef = jsonDoc.json.bufferViews![imageDef.bufferView!]
const bufferDef = jsonDoc.json.buffers![bufferViewDef.buffer]
const bufferData = bufferDef.uri ? jsonDoc.resources[bufferDef.uri] : jsonDoc.resources[GLB_BUFFER]
const byteOffset = bufferViewDef.byteOffset || 0
const byteLength = bufferViewDef.byteLength
const imageData = bufferData.slice(byteOffset, byteOffset + byteLength)
const texture = this.document.createTexture(imageDef.name)
texture.setImage(imageData)
this._texturesRef.push([buffer, texture])
}
*/
}
});
return this;
}
write(context) {
this.document.getRoot().listScenes().forEach((scene) => {
const prop = scene.getExtension(viewerGLTFExtension);
if (prop) {
const sceneDef = context.jsonDoc.json.scenes?.[context.jsonDoc.json.scene || 0]; // todo: get proper scene index, if working with multiple scenes
if (sceneDef && Object.keys(this._viewerConfig).length > 0) {
sceneDef.extensions = sceneDef.extensions || {};
/*
console.log(context.jsonDoc.json.images)
for (const [buffer, texture] of this._texturesRef) {
const imageDef = context.createPropertyDef(texture) as GLTF.IImage
context.createImageData(imageDef, texture.getImage()!, texture)
buffer.data.image = context.jsonDoc.json.images!.push(imageDef) - 1
context.imageIndexMap.set(texture, buffer.data.image)
}
console.log(context.jsonDoc.json)
*/
sceneDef.extensions[viewerGLTFExtension] = this._viewerConfig;
this._texturesRef = [];
this._viewerConfig = {};
}
}
});
return this;
}
}
WebGIViewerExtension.EXTENSION_NAME = viewerGLTFExtension;
/**
* @license
* gltf-transform-generic-ext v0.1.0
* Copyright 2022-2023 repalash <palash@shaders.app>
* MIT License
*/
class EventDispatcher {
constructor() {
this._listeners = {};
}
addEventListener(type, listener) {
const listeners = this._listeners;
if (listeners[type] === undefined) {
listeners[type] = [];
}
if (listeners[type].indexOf(listener) === -1) {
listeners[type].push(listener);
}
return this;
}
removeEventListener(type, listener) {
if (this._listeners === undefined) return this;
const listeners = this._listeners;
const listenerArray = listeners[type];
if (listenerArray !== undefined) {
const index = listenerArray.indexOf(listener);
if (index !== -1) {
listenerArray.splice(index, 1);
}
}
return this;
}
dispatchEvent(event) {
if (this._listeners === undefined) return this;
const listeners = this._listeners;
const listenerArray = listeners[event.type];
if (listenerArray !== undefined) {
// Make a copy, in case listeners are removed while iterating.
const array = listenerArray.slice(0);
for (let i = 0, l = array.length; i < l; i++) {
array[i].call(this, event);
}
}
return this;
}
dispose() {
for (const key in this._listeners) {
delete this._listeners[key];
}
}
}
/**
* Represents a connection between two {@link GraphNode} resources in a {@link Graph}.
*
* The left node is considered the owner, and the right node the resource. The
* owner is responsible for being able find and remove a reference to a resource, given
* that link. The resource does not hold a reference to the link or to the owner,
* although that reverse lookup can be done on the graph.
*
* @category Graph
*/
class GraphEdge extends EventDispatcher {
constructor(_name, _parent, _child, _attributes = {}) {
super();
this._name = void 0;
this._parent = void 0;
this._child = void 0;
this._attributes = void 0;
this._disposed = false;
this._name = _name;
this._parent = _parent;
this._child = _child;
this._attributes = _attributes;
if (!_parent.isOnGraph(_child)) {
throw new Error('Cannot connect disconnected graphs.');
}
}
/** Name. */
getName() {
return this._name;
}
/** Owner node. */
getParent() {
return this._parent;
}
/** Resource node. */
getChild() {
return this._child;
}
/**
* Sets the child node.
*
* @internal Only {@link Graph} implementations may safely call this method directly. Use
* {@link Property.swap} or {@link Graph.swapChild} instead.
*/
setChild(child) {
this._child = child;
return this;
}
/** Attributes of the graph node relationship. */
getAttributes() {
return this._attributes;
}
/** Destroys a (currently intact) edge, updating both the graph and the owner. */
dispose() {
if (this._disposed) return;
this._disposed = true;
this.dispatchEvent({
type: 'dispose',
target: this
});
super.dispose();
}
/** Whether this link has been destroyed. */
isDisposed() {
return this._disposed;
}
}
function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function isRef(value) {
return value instanceof GraphEdge;
}
function isRefList(value) {
return Array.isArray(value) && value[0] instanceof GraphEdge;
}
function isRefMap(value) {
return !!(isPlainObject(value) && getFirstValue(value) instanceof GraphEdge);
}
function getFirstValue(value) {
for (const key in value) {
return value[key];
}
}
function isPlainObject(value) {
return Boolean(value) && Object.getPrototypeOf(value) === Object.prototype;
}
const $attributes = Symbol('attributes');
const $immutableKeys = Symbol('immutableKeys');
/**
* Represents a node in a {@link Graph}.
*
* @category Graph
*/
class GraphNode extends EventDispatcher {
/**
* Internal graph used to search and maintain references.
* @hidden
*/
/**
* Attributes (literal values and GraphNode references) associated with this instance. For each
* GraphNode reference, the attributes stores a {@link GraphEdge}. List and Map references are
* stored as arrays and dictionaries of edges.
* @internal
*/
/**
* Attributes included with `getDefaultAttributes` are considered immutable, and cannot be
* modifed by `.setRef()`, `.copy()`, or other GraphNode methods. Both the edges and the
* properties will be disposed with the parent GraphNode.
*
* Currently, only single-edge references (getRef/setRef) are supported as immutables.
*
* @internal
*/
constructor(graph) {
super();
this._disposed = false;
this.graph = void 0;
this[$attributes] = void 0;
this[$immutableKeys] = void 0;
this.graph = graph;
this[$immutableKeys] = new Set();
this[$attributes] = this._createAttributes();
}
/**
* Returns default attributes for the graph node. Subclasses having any attributes (either
* literal values or references to other graph nodes) must override this method. Literal
* attributes should be given their default values, if any. References should generally be
* initialized as empty (Ref → null, RefList → [], RefMap → {}) and then modified by setters.
*
* Any single-edge references (setRef) returned by this method will be considered immutable,
* to be owned by and disposed with the parent node. Multi-edge references (addRef, removeRef,
* setRefMap) cannot be returned as default attributes.
*/
getDefaults() {
return {};
}
/**
* Constructs and returns an object used to store a graph nodes attributes. Compared to the
* default Attributes interface, this has two distinctions:
*
* 1. Slots for GraphNode<T> objects are replaced with slots for GraphEdge<this, GraphNode<T>>
* 2. GraphNode<T> objects provided as defaults are considered immutable
*
* @internal
*/
_createAttributes() {
const defaultAttributes = this.getDefaults();
const attributes = {};
for (const key in defaultAttributes) {
const value = defaultAttributes[key];
if (value instanceof GraphNode) {
const ref = this.graph.createEdge(key, this, value);
ref.addEventListener('dispose', () => value.dispose());
this[$immutableKeys].add(key);
attributes[key] = ref;
} else {
attributes[key] = value;
}
}
return attributes;
}
/** @internal Returns true if two nodes are on the same {@link Graph}. */
isOnGraph(other) {
return this.graph === other.graph;
}
/** Returns true if the node has been permanently removed from the graph. */
isDisposed() {
return this._disposed;
}
/**
* Removes both inbound references to and outbound references from this object. At the end
* of the process the object holds no references, and nothing holds references to it. A
* disposed object is not reusable.
*/
dispose() {
if (this._disposed) return;
this.graph.listChildEdges(this).forEach(edge => edge.dispose());
this.graph.disconnectParents(this);
this._disposed = true;
this.dispatchEvent({
type: 'dispose'
});
}
/**
* Removes all inbound references to this object. At the end of the process the object is
* considered 'detached': it may hold references to child resources, but nothing holds
* references to it. A detached object may be re-attached.
*/
detach() {
this.graph.disconnectParents(this);
return this;
}
/**
* Transfers this object's references from the old node to the new one. The old node is fully
* detached from this parent at the end of the process.
*
* @hidden
*/
swap(old, replacement) {
for (const attribute in this[$attributes]) {
const value = this[$attributes][attribute];
if (isRef(value)) {
const ref = value;
if (ref.getChild() === old) {
this.setRef(attribute, replacement, ref.getAttributes());
}
} else if (isRefList(value)) {
const refs = value;
const ref = refs.find(ref => ref.getChild() === old);
if (ref) {
const refAttributes = ref.getAttributes();
this.removeRef(attribute, old).addRef(attribute, replacement, refAttributes);
}
} else if (isRefMap(value)) {
const refMap = value;
for (const key in refMap) {
const ref = refMap[key];
if (ref.getChild() === old) {
this.setRefMap(attribute, key, replacement, ref.getAttributes());
}
}
}
}
return this;
}
/**********************************************************************************************
* Literal attributes.
*/
/** @hidden */
get(attribute) {
return this[$attributes][attribute];
}
/** @hidden */
set(attribute, value) {
this[$attributes][attribute] = value;
return this.dispatchEvent({
type: 'change',
attribute
});
}
/**********************************************************************************************
* Ref: 1:1 graph node references.
*/
/** @hidden */
getRef(attribute) {
const ref = this[$attributes][attribute];
return ref ? ref.getChild() : null;
}
/** @hidden */
setRef(attribute, value, attributes) {
if (this[$immutableKeys].has(at