manifold-3d
Version:
Geometry library for topological robustness
1,371 lines (1,369 loc) • 1.72 MB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
// node_modules/property-graph/dist/index.mjs
var EventDispatcher, GraphEdge, Graph, RefList, RefSet, RefMap, $attributes, $immutableKeys, GraphNode;
var init_dist = __esm({
"node_modules/property-graph/dist/index.mjs"() {
EventDispatcher = class {
_listeners = {};
addEventListener(type, listener) {
const listeners = this._listeners;
if (listeners[type] === void 0) listeners[type] = [];
if (listeners[type].indexOf(listener) === -1) listeners[type].push(listener);
return this;
}
removeEventListener(type, listener) {
const listenerArray = this._listeners[type];
if (listenerArray !== void 0) {
const index = listenerArray.indexOf(listener);
if (index !== -1) listenerArray.splice(index, 1);
}
return this;
}
dispatchEvent(event) {
const listenerArray = this._listeners[event.type];
if (listenerArray !== void 0) {
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];
}
};
GraphEdge = class {
_disposed = false;
_name;
_parent;
_child;
_attributes;
constructor(_name, _parent, _child, _attributes = {}) {
this._name = _name;
this._parent = _parent;
this._child = _child;
this._attributes = _attributes;
if (!_parent.isOnGraph(_child)) throw new Error("Cannot connect disconnected graphs.");
}
/** Name (attribute name from parent {@link GraphNode}). */
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._parent._destroyRef(this);
this._disposed = true;
}
/** Whether this link has been destroyed. */
isDisposed() {
return this._disposed;
}
};
Graph = class extends EventDispatcher {
_emptySet = /* @__PURE__ */ new Set();
_edges = /* @__PURE__ */ new Set();
_parentEdges = /* @__PURE__ */ new Map();
_childEdges = /* @__PURE__ */ new Map();
/** Returns a list of all parent->child edges on this graph. */
listEdges() {
return Array.from(this._edges);
}
/** Returns a list of all edges on the graph having the given node as their child. */
listParentEdges(node) {
return Array.from(this._childEdges.get(node) || this._emptySet);
}
/** Returns a list of parent nodes for the given child node. */
listParents(node) {
const parentSet = /* @__PURE__ */ new Set();
for (const edge of this.listParentEdges(node)) parentSet.add(edge.getParent());
return Array.from(parentSet);
}
/** Returns a list of all edges on the graph having the given node as their parent. */
listChildEdges(node) {
return Array.from(this._parentEdges.get(node) || this._emptySet);
}
/** Returns a list of child nodes for the given parent node. */
listChildren(node) {
const childSet = /* @__PURE__ */ new Set();
for (const edge of this.listChildEdges(node)) childSet.add(edge.getChild());
return Array.from(childSet);
}
disconnectParents(node, filter) {
for (const edge of this.listParentEdges(node)) if (!filter || filter(edge.getParent())) edge.dispose();
return this;
}
/**********************************************************************************************
* Internal.
*/
/**
* Creates a {@link GraphEdge} connecting two {@link GraphNode} instances. Edge is returned
* for the caller to store.
* @param a Owner
* @param b Resource
* @hidden
* @internal
*/
_createEdge(name, a, b, attributes) {
const edge = new GraphEdge(name, a, b, attributes);
this._edges.add(edge);
const parent = edge.getParent();
if (!this._parentEdges.has(parent)) this._parentEdges.set(parent, /* @__PURE__ */ new Set());
this._parentEdges.get(parent).add(edge);
const child = edge.getChild();
if (!this._childEdges.has(child)) this._childEdges.set(child, /* @__PURE__ */ new Set());
this._childEdges.get(child).add(edge);
return edge;
}
/**
* Detaches a {@link GraphEdge} from the {@link Graph}. Before calling this
* method, ensure that the GraphEdge has first been detached from any
* associated {@link GraphNode} attributes.
* @hidden
* @internal
*/
_destroyEdge(edge) {
this._edges.delete(edge);
this._parentEdges.get(edge.getParent()).delete(edge);
this._childEdges.get(edge.getChild()).delete(edge);
return this;
}
};
RefList = class {
list = [];
constructor(refs) {
if (refs) for (const ref of refs) this.list.push(ref);
}
add(ref) {
this.list.push(ref);
}
remove(ref) {
const index = this.list.indexOf(ref);
if (index >= 0) this.list.splice(index, 1);
}
removeChild(child) {
const refs = [];
for (const ref of this.list) if (ref.getChild() === child) refs.push(ref);
for (const ref of refs) this.remove(ref);
return refs;
}
listRefsByChild(child) {
const refs = [];
for (const ref of this.list) if (ref.getChild() === child) refs.push(ref);
return refs;
}
values() {
return this.list;
}
};
RefSet = class {
set = /* @__PURE__ */ new Set();
map = /* @__PURE__ */ new Map();
constructor(refs) {
if (refs) for (const ref of refs) this.add(ref);
}
add(ref) {
const child = ref.getChild();
this.removeChild(child);
this.set.add(ref);
this.map.set(child, ref);
}
remove(ref) {
this.set.delete(ref);
this.map.delete(ref.getChild());
}
removeChild(child) {
const ref = this.map.get(child) || null;
if (ref) this.remove(ref);
return ref;
}
getRefByChild(child) {
return this.map.get(child) || null;
}
values() {
return Array.from(this.set);
}
};
RefMap = class {
map = {};
constructor(map) {
if (map) Object.assign(this.map, map);
}
set(key, child) {
this.map[key] = child;
}
delete(key) {
delete this.map[key];
}
get(key) {
return this.map[key] || null;
}
keys() {
return Object.keys(this.map);
}
values() {
return Object.values(this.map);
}
};
$attributes = /* @__PURE__ */ Symbol("attributes");
$immutableKeys = /* @__PURE__ */ Symbol("immutableKeys");
GraphNode = class GraphNode2 extends EventDispatcher {
_disposed = false;
/**
* Internal graph used to search and maintain references.
* @hidden
*/
graph;
/**
* 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];
/**
* 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
*/
[$immutableKeys];
constructor(graph) {
super();
this.graph = graph;
this[$immutableKeys] = /* @__PURE__ */ 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 value2 = defaultAttributes[key];
if (value2 instanceof GraphNode2) {
const ref = this.graph._createEdge(key, this, value2);
this[$immutableKeys].add(key);
attributes[key] = ref;
} else attributes[key] = value2;
}
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(prevValue, nextValue) {
for (const attribute in this[$attributes]) {
const value2 = this[$attributes][attribute];
if (value2 instanceof GraphEdge) {
const ref = value2;
if (ref.getChild() === prevValue) this.setRef(attribute, nextValue, ref.getAttributes());
} else if (value2 instanceof RefList) for (const ref of value2.listRefsByChild(prevValue)) {
const refAttributes = ref.getAttributes();
this.removeRef(attribute, prevValue);
this.addRef(attribute, nextValue, refAttributes);
}
else if (value2 instanceof RefSet) {
const ref = value2.getRefByChild(prevValue);
if (ref) {
const refAttributes = ref.getAttributes();
this.removeRef(attribute, prevValue);
this.addRef(attribute, nextValue, refAttributes);
}
} else if (value2 instanceof RefMap) for (const key of value2.keys()) {
const ref = value2.get(key);
if (ref.getChild() === prevValue) this.setRefMap(attribute, key, nextValue, ref.getAttributes());
}
}
return this;
}
/**********************************************************************************************
* Literal attributes.
*/
/** @hidden */
get(attribute) {
return this[$attributes][attribute];
}
/** @hidden */
set(attribute, value2) {
this[$attributes][attribute] = value2;
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, value2, attributes) {
if (this[$immutableKeys].has(attribute)) throw new Error(`Cannot overwrite immutable attribute, "${attribute}".`);
const prevRef = this[$attributes][attribute];
if (prevRef) prevRef.dispose();
if (!value2) return this;
const ref = this.graph._createEdge(attribute, this, value2, attributes);
this[$attributes][attribute] = ref;
return this.dispatchEvent({
type: "change",
attribute
});
}
/**********************************************************************************************
* RefList: 1:many graph node references.
*/
/** @hidden */
listRefs(attribute) {
return this.assertRefList(attribute).values().map((ref) => ref.getChild());
}
/** @hidden */
addRef(attribute, value2, attributes) {
const ref = this.graph._createEdge(attribute, this, value2, attributes);
this.assertRefList(attribute).add(ref);
return this.dispatchEvent({
type: "change",
attribute
});
}
/** @hidden */
removeRef(attribute, value2) {
const refs = this.assertRefList(attribute);
if (refs instanceof RefList) for (const ref of refs.listRefsByChild(value2)) ref.dispose();
else {
const ref = refs.getRefByChild(value2);
if (ref) ref.dispose();
}
return this;
}
/** @hidden */
assertRefList(attribute) {
const refs = this[$attributes][attribute];
if (refs instanceof RefList || refs instanceof RefSet) return refs;
throw new Error(`Expected RefList or RefSet for attribute "${attribute}"`);
}
/**********************************************************************************************
* RefMap: Named 1:many (map) graph node references.
*/
/** @hidden */
listRefMapKeys(attribute) {
return this.assertRefMap(attribute).keys();
}
/** @hidden */
listRefMapValues(attribute) {
return this.assertRefMap(attribute).values().map((ref) => ref.getChild());
}
/** @hidden */
getRefMap(attribute, key) {
const ref = this.assertRefMap(attribute).get(key);
return ref ? ref.getChild() : null;
}
/** @hidden */
setRefMap(attribute, key, value2, metadata) {
const refMap = this.assertRefMap(attribute);
const prevRef = refMap.get(key);
if (prevRef) prevRef.dispose();
if (!value2) return this;
metadata = Object.assign(metadata || {}, { key });
const ref = this.graph._createEdge(attribute, this, value2, {
...metadata,
key
});
refMap.set(key, ref);
return this.dispatchEvent({
type: "change",
attribute,
key
});
}
/** @hidden */
assertRefMap(attribute) {
const map = this[$attributes][attribute];
if (map instanceof RefMap) return map;
throw new Error(`Expected RefMap for attribute "${attribute}"`);
}
/**********************************************************************************************
* Events.
*/
/**
* Dispatches an event on the GraphNode, and on the associated
* Graph. Event types on the graph are prefixed, `"node:[type]"`.
*/
dispatchEvent(event) {
super.dispatchEvent({
...event,
target: this
});
this.graph.dispatchEvent({
...event,
target: this,
type: `node:${event.type}`
});
return this;
}
/**********************************************************************************************
* Internal.
*/
/** @hidden */
_destroyRef(ref) {
const attribute = ref.getName();
if (this[$attributes][attribute] === ref) {
this[$attributes][attribute] = null;
if (this[$immutableKeys].has(attribute)) ref.getChild().dispose();
} else if (this[$attributes][attribute] instanceof RefList) this[$attributes][attribute].remove(ref);
else if (this[$attributes][attribute] instanceof RefSet) this[$attributes][attribute].remove(ref);
else if (this[$attributes][attribute] instanceof RefMap) {
const refMap = this[$attributes][attribute];
for (const key of refMap.keys()) if (refMap.get(key) === ref) refMap.delete(key);
} else return;
this.graph._destroyEdge(ref);
this.dispatchEvent({
type: "change",
attribute
});
}
};
}
});
// node_modules/@gltf-transform/core/dist/index.modern.js
function validateJPEGBuffer(view, i) {
if (i > view.byteLength) {
throw new TypeError("Corrupt JPG, exceeded buffer limits");
}
if (view.getUint8(i) !== 255) {
throw new TypeError("Invalid JPG, marker table corrupted");
}
return view;
}
function create() {
var out = new ARRAY_TYPE(3);
if (ARRAY_TYPE != Float32Array) {
out[0] = 0;
out[1] = 0;
out[2] = 0;
}
return out;
}
function length(a) {
var x = a[0];
var y = a[1];
var z = a[2];
return Math.sqrt(x * x + y * y + z * z);
}
function transformMat4(out, a, m) {
var x = a[0], y = a[1], z = a[2];
var w = m[3] * x + m[7] * y + m[11] * z + m[15];
w = w || 1;
out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w;
out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w;
out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w;
return out;
}
function getBounds(node) {
const resultBounds = createBounds();
const parents = node.propertyType === PropertyType.NODE ? [node] : node.listChildren();
for (const parent of parents) {
parent.traverse((node2) => {
const mesh = node2.getMesh();
if (!mesh) return;
const meshBounds = getMeshBounds(mesh, node2.getWorldMatrix());
if (meshBounds.min.every(isFinite) && meshBounds.max.every(isFinite)) {
expandBounds(meshBounds.min, resultBounds);
expandBounds(meshBounds.max, resultBounds);
}
});
}
return resultBounds;
}
function getMeshBounds(mesh, worldMatrix) {
const meshBounds = createBounds();
for (const prim of mesh.listPrimitives()) {
const position = prim.getAttribute("POSITION");
const indices = prim.getIndices();
if (!position) continue;
let localPos = [0, 0, 0];
let worldPos = [0, 0, 0];
for (let i = 0, il = indices ? indices.getCount() : position.getCount(); i < il; i++) {
const index = indices ? indices.getScalar(i) : i;
localPos = position.getElement(index, localPos);
worldPos = transformMat4(worldPos, localPos, worldMatrix);
expandBounds(worldPos, meshBounds);
}
}
return meshBounds;
}
function expandBounds(point, target) {
for (let i = 0; i < 3; i++) {
target.min[i] = Math.min(point[i], target.min[i]);
target.max[i] = Math.max(point[i], target.max[i]);
}
}
function createBounds() {
return {
min: [Infinity, Infinity, Infinity],
max: [-Infinity, -Infinity, -Infinity]
};
}
function isObject(o) {
return Object.prototype.toString.call(o) === "[object Object]";
}
function isPlainObject(o) {
if (isObject(o) === false) return false;
const ctor = o.constructor;
if (ctor === void 0) return true;
const prot = ctor.prototype;
if (isObject(prot) === false) return false;
if (Object.hasOwn(prot, "isPrototypeOf") === false) {
return false;
}
return true;
}
function determinant(a) {
var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
var a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
var b0 = a00 * a11 - a01 * a10;
var b1 = a00 * a12 - a02 * a10;
var b22 = a01 * a12 - a02 * a11;
var b3 = a20 * a31 - a21 * a30;
var b42 = a20 * a32 - a22 * a30;
var b5 = a21 * a32 - a22 * a31;
var b6 = a00 * b5 - a01 * b42 + a02 * b3;
var b7 = a10 * b5 - a11 * b42 + a12 * b3;
var b82 = a20 * b22 - a21 * b1 + a22 * b0;
var b9 = a30 * b22 - a31 * b1 + a32 * b0;
return a13 * b6 - a03 * b7 + a33 * b82 - a23 * b9;
}
function multiply(out, a, b) {
var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
var a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
var a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
var a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
var b0 = b[0], b1 = b[1], b22 = b[2], b3 = b[3];
out[0] = b0 * a00 + b1 * a10 + b22 * a20 + b3 * a30;
out[1] = b0 * a01 + b1 * a11 + b22 * a21 + b3 * a31;
out[2] = b0 * a02 + b1 * a12 + b22 * a22 + b3 * a32;
out[3] = b0 * a03 + b1 * a13 + b22 * a23 + b3 * a33;
b0 = b[4];
b1 = b[5];
b22 = b[6];
b3 = b[7];
out[4] = b0 * a00 + b1 * a10 + b22 * a20 + b3 * a30;
out[5] = b0 * a01 + b1 * a11 + b22 * a21 + b3 * a31;
out[6] = b0 * a02 + b1 * a12 + b22 * a22 + b3 * a32;
out[7] = b0 * a03 + b1 * a13 + b22 * a23 + b3 * a33;
b0 = b[8];
b1 = b[9];
b22 = b[10];
b3 = b[11];
out[8] = b0 * a00 + b1 * a10 + b22 * a20 + b3 * a30;
out[9] = b0 * a01 + b1 * a11 + b22 * a21 + b3 * a31;
out[10] = b0 * a02 + b1 * a12 + b22 * a22 + b3 * a32;
out[11] = b0 * a03 + b1 * a13 + b22 * a23 + b3 * a33;
b0 = b[12];
b1 = b[13];
b22 = b[14];
b3 = b[15];
out[12] = b0 * a00 + b1 * a10 + b22 * a20 + b3 * a30;
out[13] = b0 * a01 + b1 * a11 + b22 * a21 + b3 * a31;
out[14] = b0 * a02 + b1 * a12 + b22 * a22 + b3 * a32;
out[15] = b0 * a03 + b1 * a13 + b22 * a23 + b3 * a33;
return out;
}
function getScaling(out, mat) {
var m11 = mat[0];
var m12 = mat[1];
var m13 = mat[2];
var m21 = mat[4];
var m22 = mat[5];
var m23 = mat[6];
var m31 = mat[8];
var m32 = mat[9];
var m33 = mat[10];
out[0] = Math.sqrt(m11 * m11 + m12 * m12 + m13 * m13);
out[1] = Math.sqrt(m21 * m21 + m22 * m22 + m23 * m23);
out[2] = Math.sqrt(m31 * m31 + m32 * m32 + m33 * m33);
return out;
}
function getRotation(out, mat) {
var scaling = new ARRAY_TYPE(3);
getScaling(scaling, mat);
var is1 = 1 / scaling[0];
var is2 = 1 / scaling[1];
var is3 = 1 / scaling[2];
var sm11 = mat[0] * is1;
var sm12 = mat[1] * is2;
var sm13 = mat[2] * is3;
var sm21 = mat[4] * is1;
var sm22 = mat[5] * is2;
var sm23 = mat[6] * is3;
var sm31 = mat[8] * is1;
var sm32 = mat[9] * is2;
var sm33 = mat[10] * is3;
var trace = sm11 + sm22 + sm33;
var S = 0;
if (trace > 0) {
S = Math.sqrt(trace + 1) * 2;
out[3] = 0.25 * S;
out[0] = (sm23 - sm32) / S;
out[1] = (sm31 - sm13) / S;
out[2] = (sm12 - sm21) / S;
} else if (sm11 > sm22 && sm11 > sm33) {
S = Math.sqrt(1 + sm11 - sm22 - sm33) * 2;
out[3] = (sm23 - sm32) / S;
out[0] = 0.25 * S;
out[1] = (sm12 + sm21) / S;
out[2] = (sm31 + sm13) / S;
} else if (sm22 > sm33) {
S = Math.sqrt(1 + sm22 - sm11 - sm33) * 2;
out[3] = (sm31 - sm13) / S;
out[0] = (sm12 + sm21) / S;
out[1] = 0.25 * S;
out[2] = (sm23 + sm32) / S;
} else {
S = Math.sqrt(1 + sm33 - sm11 - sm22) * 2;
out[3] = (sm12 - sm21) / S;
out[0] = (sm31 + sm13) / S;
out[1] = (sm23 + sm32) / S;
out[2] = 0.25 * S;
}
return out;
}
function equalsRef(refA, refB) {
if (!!refA !== !!refB) return false;
const a = refA.getChild();
const b = refB.getChild();
return a === b || a.equals(b);
}
function equalsRefSet(refSetA, refSetB) {
if (!!refSetA !== !!refSetB) return false;
const refValuesA = refSetA.values();
const refValuesB = refSetB.values();
if (refValuesA.length !== refValuesB.length) return false;
for (let i = 0; i < refValuesA.length; i++) {
const a = refValuesA[i];
const b = refValuesB[i];
if (a.getChild() === b.getChild()) continue;
if (!a.getChild().equals(b.getChild())) return false;
}
return true;
}
function equalsRefMap(refMapA, refMapB) {
if (!!refMapA !== !!refMapB) return false;
const keysA = refMapA.keys();
const keysB = refMapB.keys();
if (keysA.length !== keysB.length) return false;
for (const key of keysA) {
const refA = refMapA.get(key);
const refB = refMapB.get(key);
if (!!refA !== !!refB) return false;
const a = refA.getChild();
const b = refB.getChild();
if (a === b) continue;
if (!a.equals(b)) return false;
}
return true;
}
function equalsArray(a, b) {
if (a === b) return true;
if (!!a !== !!b || !a || !b) return false;
if (a.length !== b.length) return false;
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) return false;
}
return true;
}
function equalsObject(_a2, _b2) {
if (_a2 === _b2) return true;
if (!!_a2 !== !!_b2) return false;
if (!isPlainObject(_a2) || !isPlainObject(_b2)) {
return _a2 === _b2;
}
const a = _a2;
const b = _b2;
let numKeysA = 0;
let numKeysB = 0;
let key;
for (key in a) numKeysA++;
for (key in b) numKeysB++;
if (numKeysA !== numKeysB) return false;
for (key in a) {
const valueA = a[key];
const valueB = b[key];
if (isArray(valueA) && isArray(valueB)) {
if (!equalsArray(valueA, valueB)) return false;
} else if (isPlainObject(valueA) && isPlainObject(valueB)) {
if (!equalsObject(valueA, valueB)) return false;
} else {
if (valueA !== valueB) return false;
}
}
return true;
}
function isArray(value2) {
return Array.isArray(value2) || ArrayBuffer.isView(value2);
}
function arrayToComponentType(array) {
switch (array.constructor) {
case Float32Array:
return Accessor.ComponentType.FLOAT;
case Uint32Array:
return Accessor.ComponentType.UNSIGNED_INT;
case Uint16Array:
return Accessor.ComponentType.UNSIGNED_SHORT;
case Uint8Array:
return Accessor.ComponentType.UNSIGNED_BYTE;
case Int16Array:
return Accessor.ComponentType.SHORT;
case Int8Array:
return Accessor.ComponentType.BYTE;
default:
throw new Error("Unknown accessor componentType.");
}
}
function _extends() {
return _extends = Object.assign ? Object.assign.bind() : function(n2) {
for (var e = 1; e < arguments.length; e++) {
var t = arguments[e];
for (var r in t) ({}).hasOwnProperty.call(t, r) && (n2[r] = t[r]);
}
return n2;
}, _extends.apply(null, arguments);
}
function getInterleavedArray(accessorDef, context) {
const jsonDoc = context.jsonDoc;
const bufferView = context.bufferViews[accessorDef.bufferView];
const bufferViewDef = jsonDoc.json.bufferViews[accessorDef.bufferView];
const TypedArray = ComponentTypeToTypedArray[accessorDef.componentType];
const elementSize = Accessor.getElementSize(accessorDef.type);
const componentSize = TypedArray.BYTES_PER_ELEMENT;
const accessorByteOffset = accessorDef.byteOffset || 0;
const array = new TypedArray(accessorDef.count * elementSize);
const view = new DataView(bufferView.buffer, bufferView.byteOffset, bufferView.byteLength);
const byteStride = bufferViewDef.byteStride;
for (let i = 0; i < accessorDef.count; i++) {
for (let j = 0; j < elementSize; j++) {
const byteOffset = accessorByteOffset + i * byteStride + j * componentSize;
let value2;
switch (accessorDef.componentType) {
case Accessor.ComponentType.FLOAT:
value2 = view.getFloat32(byteOffset, true);
break;
case Accessor.ComponentType.UNSIGNED_INT:
value2 = view.getUint32(byteOffset, true);
break;
case Accessor.ComponentType.UNSIGNED_SHORT:
value2 = view.getUint16(byteOffset, true);
break;
case Accessor.ComponentType.UNSIGNED_BYTE:
value2 = view.getUint8(byteOffset);
break;
case Accessor.ComponentType.SHORT:
value2 = view.getInt16(byteOffset, true);
break;
case Accessor.ComponentType.BYTE:
value2 = view.getInt8(byteOffset);
break;
default:
throw new Error(`Unexpected componentType "${accessorDef.componentType}".`);
}
array[i * elementSize + j] = value2;
}
}
return array;
}
function getAccessorArray(accessorDef, context) {
const jsonDoc = context.jsonDoc;
const bufferView = context.bufferViews[accessorDef.bufferView];
const bufferViewDef = jsonDoc.json.bufferViews[accessorDef.bufferView];
const TypedArray = ComponentTypeToTypedArray[accessorDef.componentType];
const elementSize = Accessor.getElementSize(accessorDef.type);
const componentSize = TypedArray.BYTES_PER_ELEMENT;
const elementStride = elementSize * componentSize;
if (bufferViewDef.byteStride !== void 0 && bufferViewDef.byteStride !== elementStride) {
return getInterleavedArray(accessorDef, context);
}
const byteOffset = bufferView.byteOffset + (accessorDef.byteOffset || 0);
const byteLength = accessorDef.count * elementSize * componentSize;
return new TypedArray(bufferView.buffer.slice(byteOffset, byteOffset + byteLength));
}
function getSparseArray(accessorDef, context) {
const TypedArray = ComponentTypeToTypedArray[accessorDef.componentType];
const elementSize = Accessor.getElementSize(accessorDef.type);
let array;
if (accessorDef.bufferView !== void 0) {
array = getAccessorArray(accessorDef, context);
} else {
array = new TypedArray(accessorDef.count * elementSize);
}
const sparseDef = accessorDef.sparse;
if (!sparseDef) return array;
const count = sparseDef.count;
const indicesDef = _extends({}, accessorDef, sparseDef.indices, {
count,
type: "SCALAR"
});
const valuesDef = _extends({}, accessorDef, sparseDef.values, {
count
});
const indices = getAccessorArray(indicesDef, context);
const values = getAccessorArray(valuesDef, context);
for (let i = 0; i < indicesDef.count; i++) {
for (let j = 0; j < elementSize; j++) {
array[indices[i] * elementSize + j] = values[i * elementSize + j];
}
}
return array;
}
function getSlot(document, texture) {
const edge = document.getGraph().listParentEdges(texture).find((edge2) => edge2.getParent() !== document.getRoot());
return edge ? edge.getName().replace(/texture$/i, "") : "";
}
function clean(object) {
const unused = [];
for (const key in object) {
const value2 = object[key];
if (Array.isArray(value2) && value2.length === 0) {
unused.push(key);
} else if (value2 === null || value2 === "") {
unused.push(key);
} else if (value2 && typeof value2 === "object" && Object.keys(value2).length === 0) {
unused.push(key);
}
}
for (const key of unused) {
delete object[key];
}
}
function isExternalBuffer(jsonDocument, bufferDef) {
return bufferDef.uri !== void 0 && !(bufferDef.uri in jsonDocument.resources);
}
function isExternalImage(jsonDocument, imageDef) {
return imageDef.uri !== void 0 && !(imageDef.uri in jsonDocument.resources) && imageDef.bufferView === void 0;
}
function isGLB(view) {
if (view.byteLength < 3 * Uint32Array.BYTES_PER_ELEMENT) return false;
const header = new Uint32Array(view.buffer, view.byteOffset, 3);
return header[0] === 1179937895 && header[1] === 2;
}
var VERSION, GLB_BUFFER, PropertyType, VertexLayout, BufferViewUsage$1, TextureChannel, Format, ComponentTypeToTypedArray, BufferUtils, JPEGImageUtils, PNGImageUtils, ImageUtils, FileUtils, ARRAY_TYPE, NULL_DOMAIN, HTTPUtils, _Logger, Verbosity, Logger, MathUtils, ALPHABET, UNIQUE_RETRIES, ID_LENGTH, previousIDs, generateOne, uuid, COPY_IDENTITY, EMPTY_SET, Property, ExtensibleProperty, Accessor, Animation, AnimationChannel, AnimationSampler, Buffer$1, Camera, ExtensionProperty, TextureInfo, R, G, B, A, Material, Mesh, Node, Primitive, PrimitiveTarget, Scene, Skin, Texture, Root, Document, Extension, ReaderContext, DEFAULT_OPTIONS, SUPPORTED_PREREAD_TYPES, GLTFReader, BufferViewTarget, WriterContext, UniqueURIGenerator, BufferViewUsage, UNSIGNED_INT, UNSIGNED_SHORT, UNSIGNED_BYTE, SUPPORTED_PREWRITE_TYPES, GLTFWriter, ChunkType, PlatformIO, WebIO;
var init_index_modern = __esm({
"node_modules/@gltf-transform/core/dist/index.modern.js"() {
init_dist();
init_dist();
VERSION = `v${"4.3.0"}`;
GLB_BUFFER = "@glb.bin";
(function(PropertyType2) {
PropertyType2["ACCESSOR"] = "Accessor";
PropertyType2["ANIMATION"] = "Animation";
PropertyType2["ANIMATION_CHANNEL"] = "AnimationChannel";
PropertyType2["ANIMATION_SAMPLER"] = "AnimationSampler";
PropertyType2["BUFFER"] = "Buffer";
PropertyType2["CAMERA"] = "Camera";
PropertyType2["MATERIAL"] = "Material";
PropertyType2["MESH"] = "Mesh";
PropertyType2["PRIMITIVE"] = "Primitive";
PropertyType2["PRIMITIVE_TARGET"] = "PrimitiveTarget";
PropertyType2["NODE"] = "Node";
PropertyType2["ROOT"] = "Root";
PropertyType2["SCENE"] = "Scene";
PropertyType2["SKIN"] = "Skin";
PropertyType2["TEXTURE"] = "Texture";
PropertyType2["TEXTURE_INFO"] = "TextureInfo";
})(PropertyType || (PropertyType = {}));
(function(VertexLayout2) {
VertexLayout2["INTERLEAVED"] = "interleaved";
VertexLayout2["SEPARATE"] = "separate";
})(VertexLayout || (VertexLayout = {}));
(function(BufferViewUsage2) {
BufferViewUsage2["ARRAY_BUFFER"] = "ARRAY_BUFFER";
BufferViewUsage2["ELEMENT_ARRAY_BUFFER"] = "ELEMENT_ARRAY_BUFFER";
BufferViewUsage2["INVERSE_BIND_MATRICES"] = "INVERSE_BIND_MATRICES";
BufferViewUsage2["OTHER"] = "OTHER";
BufferViewUsage2["SPARSE"] = "SPARSE";
})(BufferViewUsage$1 || (BufferViewUsage$1 = {}));
(function(TextureChannel2) {
TextureChannel2[TextureChannel2["R"] = 4096] = "R";
TextureChannel2[TextureChannel2["G"] = 256] = "G";
TextureChannel2[TextureChannel2["B"] = 16] = "B";
TextureChannel2[TextureChannel2["A"] = 1] = "A";
})(TextureChannel || (TextureChannel = {}));
(function(Format2) {
Format2["GLTF"] = "GLTF";
Format2["GLB"] = "GLB";
})(Format || (Format = {}));
ComponentTypeToTypedArray = {
"5120": Int8Array,
"5121": Uint8Array,
"5122": Int16Array,
"5123": Uint16Array,
"5125": Uint32Array,
"5126": Float32Array
};
BufferUtils = class {
/** Creates a byte array from a Data URI. */
static createBufferFromDataURI(dataURI) {
if (typeof Buffer === "undefined") {
const byteString = atob(dataURI.split(",")[1]);
const ia = new Uint8Array(byteString.length);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return ia;
} else {
const data = dataURI.split(",")[1];
const isBase64 = dataURI.indexOf("base64") >= 0;
return Buffer.from(data, isBase64 ? "base64" : "utf8");
}
}
/** Encodes text to a byte array. */
static encodeText(text) {
return new TextEncoder().encode(text);
}
/** Decodes a byte array to text. */
static decodeText(array) {
return new TextDecoder().decode(array);
}
/**
* Concatenates N byte arrays.
*/
static concat(arrays) {
let totalByteLength = 0;
for (const array of arrays) {
totalByteLength += array.byteLength;
}
const result = new Uint8Array(totalByteLength);
let byteOffset = 0;
for (const array of arrays) {
result.set(array, byteOffset);
byteOffset += array.byteLength;
}
return result;
}
/**
* Pads a Uint8Array to the next 4-byte boundary.
*
* Reference: [glTF → Data Alignment](https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#data-alignment)
*/
static pad(srcArray, paddingByte = 0) {
const paddedLength = this.padNumber(srcArray.byteLength);
if (paddedLength === srcArray.byteLength) return srcArray;
const dstArray = new Uint8Array(paddedLength);
dstArray.set(srcArray);
if (paddingByte !== 0) {
for (let i = srcArray.byteLength; i < paddedLength; i++) {
dstArray[i] = paddingByte;
}
}
return dstArray;
}
/** Pads a number to 4-byte boundaries. */
static padNumber(v) {
return Math.ceil(v / 4) * 4;
}
/** Returns true if given byte array instances are equal. */
static equals(a, b) {
if (a === b) return true;
if (a.byteLength !== b.byteLength) return false;
let i = a.byteLength;
while (i--) {
if (a[i] !== b[i]) return false;
}
return true;
}
/**
* Returns a Uint8Array view of a typed array, with the same underlying ArrayBuffer.
*
* A shorthand for:
*
* ```js
* const buffer = new Uint8Array(
* array.buffer,
* array.byteOffset + byteOffset,
* Math.min(array.byteLength, byteLength)
* );
* ```
*
*/
static toView(a, byteOffset = 0, byteLength = Infinity) {
return new Uint8Array(a.buffer, a.byteOffset + byteOffset, Math.min(a.byteLength, byteLength));
}
static assertView(view) {
if (view && !ArrayBuffer.isView(view)) {
throw new Error(`Method requires Uint8Array parameter; received "${typeof view}".`);
}
return view;
}
};
JPEGImageUtils = class {
match(array) {
return array.length >= 3 && array[0] === 255 && array[1] === 216 && array[2] === 255;
}
getSize(array) {
let view = new DataView(array.buffer, array.byteOffset + 4);
let i, next;
while (view.byteLength) {
i = view.getUint16(0, false);
validateJPEGBuffer(view, i);
next = view.getUint8(i + 1);
if (next === 192 || next === 193 || next === 194) {
return [view.getUint16(i + 7, false), view.getUint16(i + 5, false)];
}
view = new DataView(array.buffer, view.byteOffset + i + 2);
}
throw new TypeError("Invalid JPG, no size found");
}
getChannels(_buffer) {
return 3;
}
};
PNGImageUtils = class _PNGImageUtils {
match(array) {
return array.length >= 8 && array[0] === 137 && array[1] === 80 && array[2] === 78 && array[3] === 71 && array[4] === 13 && array[5] === 10 && array[6] === 26 && array[7] === 10;
}
getSize(array) {
const view = new DataView(array.buffer, array.byteOffset);
const magic = BufferUtils.decodeText(array.slice(12, 16));
if (magic === _PNGImageUtils.PNG_FRIED_CHUNK_NAME) {
return [view.getUint32(32, false), view.getUint32(36, false)];
}
return [view.getUint32(16, false), view.getUint32(20, false)];
}
getChannels(_buffer) {
return 4;
}
};
PNGImageUtils.PNG_FRIED_CHUNK_NAME = "CgBI";
ImageUtils = class {
/** Registers support for a new image format; useful for certain extensions. */
static registerFormat(mimeType, impl) {
this.impls[mimeType] = impl;
}
/**
* Returns detected MIME type of the given image buffer. Note that for image
* formats with support provided by extensions, the extension must be
* registered with an I/O class before it can be detected by ImageUtils.
*/
static getMimeType(buffer) {
for (const mimeType in this.impls) {
if (this.impls[mimeType].match(buffer)) {
return mimeType;
}
}
return null;
}
/** Returns the dimensions of the image. */
static getSize(buffer, mimeType) {
if (!this.impls[mimeType]) return null;
return this.impls[mimeType].getSize(buffer);
}
/**
* Returns a conservative estimate of the number of channels in the image. For some image
* formats, the method may return 4 indicating the possibility of an alpha channel, without
* the ability to guarantee that an alpha channel is present.
*/
static getChannels(buffer, mimeType) {
if (!this.impls[mimeType]) return null;
return this.impls[mimeType].getChannels(buffer);
}
/** Returns a conservative estimate of the GPU memory required by this image. */
static getVRAMByteLength(buffer, mimeType) {
if (!this.impls[mimeType]) return null;
if (this.impls[mimeType].getVRAMByteLength) {
return this.impls[mimeType].getVRAMByteLength(buffer);
}
let uncompressedBytes = 0;
const channels = 4;
const resolution = this.getSize(buffer, mimeType);
if (!resolution) return null;
while (resolution[0] > 1 || resolution[1] > 1) {
uncompressedBytes += resolution[0] * resolution[1] * channels;
resolution[0] = Math.max(Math.floor(resolution[0] / 2), 1);
resolution[1] = Math.max(Math.floor(resolution[1] / 2), 1);
}
uncompressedBytes += 1 * 1 * channels;
return uncompressedBytes;
}
/** Returns the preferred file extension for the given MIME type. */
static mimeTypeToExtension(mimeType) {
if (mimeType === "image/jpeg") return "jpg";
return mimeType.split("/").pop();
}
/** Returns the MIME type for the given file extension. */
static extensionToMimeType(extension) {
if (extension === "jpg") return "image/jpeg";
if (!extension) return "";
return `image/${extension}`;
}
};
ImageUtils.impls = {
"image/jpeg": new JPEGImageUtils(),
"image/png": new PNGImageUtils()
};
FileUtils = class {
/**
* Extracts the basename from a file path, e.g. "folder/model.glb" -> "model".
* See: {@link HTTPUtils.basename}
*/
static basename(uri) {
const fileName = uri.split(/[\\/]/).pop();
return fileName.substring(0, fileName.lastIndexOf("."));
}
/**
* Extracts the extension from a file path, e.g. "folder/model.glb" -> "glb".
* See: {@link HTTPUtils.extension}
*/
static extension(uri) {
if (uri.startsWith("data:image/")) {
const mimeType = uri.match(/data:(image\/\w+)/)[1];
return ImageUtils.mimeTypeToExtension(mimeType);
} else if (uri.startsWith("data:model/gltf+json")) {
return "gltf";
} else if (uri.startsWith("data:model/gltf-binary")) {
return "glb";
} else if (uri.startsWith("data:application/")) {
return "bin";
}
return uri.split(/[\\/]/).pop().split(/[.]/).pop();
}
};
ARRAY_TYPE = typeof Float32Array !== "undefined" ? Float32Array : Array;
(function() {
var vec = create();
return function(a, stride, offset, count, fn, arg) {
var i, l;
if (!stride) {
stride = 3;
}
if (!offset) {
offset = 0;
}
if (count) {
l = Math.min(count * stride + offset, a.length);
} else {
l = a.length;
}
for (i = offset; i < l; i += stride) {
vec[0] = a[i];
vec[1] = a[i + 1];
vec[2] = a[i + 2];
fn(vec, vec, arg);
a[i] = vec[0];
a[i + 1] = vec[1];
a[i + 2] = vec[2];
}
return a;
};
})();
NULL_DOMAIN = "https://null.example";
HTTPUtils = class {
static dirname(path) {
const index = path.lastIndexOf("/");
if (index === -1) return "./";
return path.substring(0, index + 1);
}
/**
* Extracts the basename from a URL, e.g. "folder/model.glb" -> "model".
* See: {@link FileUtils.basename}
*/
static basename(uri) {
return FileUtils.basename(new URL(uri, NULL_DOMAIN).pathname);
}
/**
* Extracts the extension from a URL, e.g. "folder/model.glb" -> "glb".
* See: {@link FileUtils.extension}
*/
static extension(uri) {
return FileUtils.extension(new URL(uri, NULL_DOMAIN).pathname);
}
static resolve(base, path) {
if (!this.isRelativePath(path)) return path;
const stack = base.split("/");
const parts = path.split("/");
stack.pop();
for (let i = 0; i < parts.length; i++) {
if (parts[i] === ".") continue;
if (parts[i] === "..") {
stack.pop();
} else {
stack.push(parts[i]);
}
}
return stack.join("/");
}
/**
* Returns true for URLs containing a protocol, and false for both
* absolute and relative paths.
*/
static isAbsoluteURL(path) {
return this.PROTOCOL_REGEXP.test(path);
}
/**
* Returns true for paths that are declared relative to some unknown base
* path. For example, "foo/bar/" is relative both "/foo/bar/" is not.
*/
static isRelativePath(path) {
return !/^(?:[a-zA-Z]+:)?\//.test(path);
}
};
HTTPUtils.DEFAULT_INIT = {};
HTTPUtils.PROTOCOL_REGEXP = /^[a-zA-Z]+:\/\//;
(function(Verbosity2) {
Verbosity2[Verbosity2["SILENT"] = 4] = "SILENT";
Verbosity2[Verbosity2["ERROR"] = 3] = "ERROR";
Verbosity2[Verbosity2["WARN"] = 2] = "WARN";
Verbosity2[Verbosity2["INFO"] = 1] = "INFO";
Verbosity2[Verbosity2["DEBUG"] = 0] = "DEBUG";
})(Verbosity || (Verbosity = {}));
Logger = class _Logger2 {
/** Constructs a new Logger instance. */
constructor(verbosity) {
this.verbosity = void 0;
this.verbosity = verbosity;
}
/** Logs an event at level {@link Logger.Verbosity.DEBUG}. */
debug(text) {
if (this.verbosity <= _Logger2.Verbosity.DEBUG) {
console.debug(text);
}
}
/** Logs an event at level {@link Logger.Verbosity.INFO}. */
info(text) {
if (this.verbosity <= _Logger2.Verbosity.INFO) {
console.info(text);
}
}
/** Logs an event at level {@link Logger.Verbosity.WARN}. */
warn(text) {
if (this.verbosity <= _Logger2.Verbosity.WARN) {
console.warn(text);
}
}
/** Logs an event at level {@link Logger.Verbosity.ERROR}. */
error(text) {
if (this.verbosity <= _Logger2.Verbosity.ERROR) {
console.error(text);
}
}
};
_Logger = Logger;
Logger.Verbosity = Verbosity;
Logger.DEFAULT_INSTANCE = new _Logger(_Logger.Verbosity.INFO);
MathUtils = class _MathUtils {
static identity(v) {
return v;
}
static eq(a, b, tolerance = 1e-5) {
if (a.length !== b.length) return false;
for (let