///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2002-2026, Open Design Alliance (the "Alliance").
// All rights reserved.
// 
// This software and its documentation and related materials are owned by
// the Alliance. The software may only be incorporated into application
// programs owned by members of the Alliance, subject to a signed
// Membership Agreement and Supplemental Software License Agreement with the
// Alliance. The structure and organization of this software are the valuable
// trade secrets of the Alliance and its suppliers. The software is also
// protected by copyright law and international treaty provisions. Application
// programs incorporating this software must include the following statement
// with their copyright notices:
// 
//   This application incorporates Open Design Alliance software pursuant to a
//   license agreement with Open Design Alliance.
//   Open Design Alliance Copyright (C) 2002-2026 by Open Design Alliance.
//   All rights reserved.
// 
// By use of this software, its documentation or related materials, you
// acknowledge and accept the above terms.
///////////////////////////////////////////////////////////////////////////////

import { Loader as Loader$1, GLTFLoadingManager, ModelImpl, loaders } from '@inweb/viewer-three';
import { Box3, Vector3, Scene, PerspectiveCamera, Group, Matrix4, BufferGeometry, BufferAttribute, MeshLambertMaterial, Mesh, LineBasicMaterial, Line, Float32BufferAttribute, MeshStandardMaterial, Color, Euler, PointsMaterial, Points, Loader, FileLoader } from 'three';
import { PCDLoader } from 'three/examples/jsm/loaders/PCDLoader.js';

const THREE = {
  Box3,
  BufferAttribute,
  BufferGeometry,
  Color,
  Euler,
  Float32BufferAttribute,
  Group,
  Line,
  LineBasicMaterial,
  Matrix4,
  Mesh,
  MeshLambertMaterial,
  MeshStandardMaterial,
  Points,
  PointsMaterial,
  PerspectiveCamera,
  Scene,
  Vector3,
};
var StackedLayerProvider = class {
  providers;
  constructor(providers) {
    this.providers = providers;
  }
  async GetLayerByURI(uri) {
    let errorStack = [];
    for (let provider of this.providers) {
      let layer = await provider.GetLayerByURI(uri);
      if (!(layer instanceof Error)) {
        return layer;
      } else {
        errorStack.push(layer);
      }
    }
    return new Error(JSON.stringify(errorStack));
  }
};
var InMemoryLayerProvider = class {
  layers;
  constructor() {
    this.layers =  new Map();
  }
  GetLayerByURI(uri) {
    if (!this.layers.has(uri)) {
      return new Error(`File with uri "${uri}" not found`);
    }
    return Promise.resolve(this.layers.get(uri));
  }
  add(file) {
    if (this.layers.has(file.header.id)) {
      throw new Error(`Inserting file with duplicate ID "${file.header.id}"`);
    }
    this.layers.set(file.header.id, file);
    return this;
  }
  AddAll(files) {
    files.forEach((f) => this.add(f));
    return this;
  }
};
function log(bla) {
  {
    console.log(`${JSON.stringify(arguments)}`);
  }
}
var FetchLayerProvider = class {
  layers;
  constructor() {
    this.layers =  new Map();
  }
  async FetchJson(url) {
    let result = await fetch(url);
    if (!result.ok) {
      return new Error(`Failed to fetch ${url}: ${result.status}`);
    }
    try {
      return await result.json();
    } catch (e) {
      log(url);
      return new Error(`Failed to parse json at ${url}: ${e}`);
    }
  }
  async GetLayerByURI(uri) {
    if (!this.layers.has(uri)) {
      let fetched = await this.FetchJson(uri);
      if (fetched instanceof Error) {
        return new Error(`File with id "${uri}" not found`);
      }
      let file = fetched;
      this.layers.set(uri, file);
      return file;
    }
    return this.layers.get(uri);
  }
};
function MMSet(map, key, value) {
  if (map.has(key)) {
    map.get(key)?.push(value);
  } else {
    map.set(key, [value]);
  }
}
var CycleError = class extends Error {};
function FindRootsOrCycles(nodes) {
  let dependencies =  new Map();
  let dependents =  new Map();
  nodes.forEach((node, path) => {
    Object.keys(node.inherits).forEach((inheritName) => {
      MMSet(dependencies, path, node.inherits[inheritName]);
      MMSet(dependents, node.inherits[inheritName], path);
    });
    Object.keys(node.children).forEach((childName) => {
      MMSet(dependencies, path, node.children[childName]);
      MMSet(dependents, node.children[childName], path);
    });
  });
  let paths = [...nodes.keys()];
  let perm = {};
  let temp = {};
  function visit(path) {
    if (perm[path]) return;
    if (temp[path]) throw new Error(`CYCLE!`);
    temp[path] = true;
    let deps = dependencies.get(path);
    if (deps) {
      deps.forEach((dep) => visit(dep));
    }
    perm[path] = true;
  }
  let roots =  new Set();
  try {
    paths.forEach((path) => {
      if (!dependents.has(path) && path.indexOf("/") === -1) {
        roots.add(path);
      }
      visit(path);
    });
  } catch (e) {
    return null;
  }
  return roots;
}
function GetHead(path) {
  return path.split("/")[0];
}
function GetTail(path) {
  let parts = path.split("/");
  parts.shift();
  return parts.join("/");
}
function MakePostCompositionNode(node) {
  return {
    node,
    children:  new Map(),
    attributes:  new Map(),
  };
}
function GetChildNodeWithPath(node, path) {
  if (path === "") return node;
  let parts = path.split("/");
  let child = node.children.get(parts[0]);
  if (child) {
    if (parts.length === 1) {
      return child;
    }
    return GetChildNodeWithPath(child, GetTail(path));
  } else {
    return null;
  }
}
function FlattenPathToPreCompositionNode(path, inputNodes) {
  let compositionNode = {
    path,
    children: {},
    inherits: {},
    attributes: {},
  };
  inputNodes.forEach((node) => {
    Object.keys(node.children).forEach((childName) => {
      compositionNode.children[childName] = node.children[childName];
    });
    Object.keys(node.inherits).forEach((inheritName) => {
      let ih = node.inherits[inheritName];
      if (ih === null) {
        delete compositionNode.inherits[inheritName];
      } else {
        compositionNode.inherits[inheritName] = ih;
      }
    });
    Object.keys(node.attributes).forEach((attrName) => {
      compositionNode.attributes[attrName] = node.attributes[attrName];
    });
  });
  return compositionNode;
}
function FlattenCompositionInput(input) {
  let compositionNodes =  new Map();
  for (let [path, inputNodes] of input) {
    compositionNodes.set(path, FlattenPathToPreCompositionNode(path, inputNodes));
  }
  return compositionNodes;
}
function ExpandFirstRootInInput(nodes) {
  let roots = FindRootsOrCycles(nodes);
  if (!roots) {
    throw new CycleError();
  }
  return ComposeNodeFromPath([...roots.values()][0], nodes);
}
function CreateArtificialRoot(nodes) {
  let roots = FindRootsOrCycles(nodes);
  if (!roots) {
    throw new CycleError();
  }
  let pseudoRoot = {
    node: "",
    attributes:  new Map(),
    children:  new Map(),
  };
  roots.forEach((root) => {
    pseudoRoot.children.set(root, ComposeNodeFromPath(root, nodes));
  });
  return pseudoRoot;
}
function ComposeNodeFromPath(path, preCompositionNodes) {
  return ComposeNode(path, MakePostCompositionNode(path), preCompositionNodes);
}
function ComposeNode(path, postCompositionNode, preCompositionNodes) {
  let preCompositionNode = preCompositionNodes.get(path);
  if (preCompositionNode) {
    AddDataFromPreComposition(preCompositionNode, postCompositionNode, preCompositionNodes);
  }
  postCompositionNode.children.forEach((child, name) => {
    ComposeNode(`${path}/${name}`, child, preCompositionNodes);
  });
  return postCompositionNode;
}
function AddDataFromPreComposition(input, node, nodes) {
  Object.values(input.inherits).forEach((inheritPath) => {
    let classNode = ComposeNodeFromPath(GetHead(inheritPath), nodes);
    let subnode = GetChildNodeWithPath(classNode, GetTail(inheritPath));
    if (!subnode) throw new Error(`Unknown node ${inheritPath}`);
    subnode.children.forEach((child, childName) => {
      node.children.set(childName, child);
    });
    for (let [attrID, attr] of subnode.attributes) {
      node.attributes.set(attrID, attr);
    }
  });
  Object.entries(input.children).forEach(([childName, child]) => {
    if (child !== null) {
      let classNode = ComposeNodeFromPath(GetHead(child), nodes);
      let subnode = GetChildNodeWithPath(classNode, GetTail(child));
      if (!subnode) throw new Error(`Unknown node ${child}`);
      node.children.set(childName, subnode);
    } else {
      node.children.delete(childName);
    }
  });
  Object.entries(input.attributes).forEach(([attrID, attr]) => {
    node.attributes.set(attrID, attr);
  });
}
var SchemaValidationError = class extends Error {};
function ValidateAttributeValue(desc, value, path, schemas) {
  if (desc.optional && value === void 0) {
    return;
  }
  if (desc.inherits) {
    desc.inherits.forEach((inheritedSchemaID) => {
      let inheritedSchema = schemas[inheritedSchemaID];
      if (!inheritedSchema) {
        throw new SchemaValidationError(`Unknown inherited schema id "${desc.inherits}"`);
      }
      ValidateAttributeValue(inheritedSchema.value, value, path, schemas);
    });
  }
  if (desc.dataType === "Boolean") {
    if (typeof value !== "boolean") {
      throw new SchemaValidationError(`Expected "${value}" to be of type boolean`);
    }
  } else if (desc.dataType === "String") {
    if (typeof value !== "string") {
      throw new SchemaValidationError(`Expected "${value}" to be of type string`);
    }
  } else if (desc.dataType === "DateTime") {
    if (typeof value !== "string") {
      throw new SchemaValidationError(`Expected "${value}" to be of type date`);
    }
  } else if (desc.dataType === "Enum") {
    if (typeof value !== "string") {
      throw new SchemaValidationError(`Expected "${value}" to be of type string`);
    }
    let found = desc.enumRestrictions.options.filter((option) => option === value).length === 1;
    if (!found) {
      throw new SchemaValidationError(`Expected "${value}" to be one of [${desc.enumRestrictions.options.join(",")}]`);
    }
  } else if (desc.dataType === "Integer") {
    if (typeof value !== "number") {
      throw new SchemaValidationError(`Expected "${value}" to be of type int`);
    }
  } else if (desc.dataType === "Real") {
    if (typeof value !== "number") {
      throw new SchemaValidationError(`Expected "${value}" to be of type real`);
    }
  } else if (desc.dataType === "Reference") {
    if (typeof value !== "string") {
      throw new SchemaValidationError(`Expected "${value}" to be of type string`);
    }
  } else if (desc.dataType === "Object") {
    if (typeof value !== "object") {
      throw new SchemaValidationError(`Expected "${value}" to be of type object`);
    }
    if (desc.objectRestrictions) {
      Object.keys(desc.objectRestrictions.values).forEach((key) => {
        let optional = desc.objectRestrictions.values[key].optional;
        let hasOwn = Object.hasOwn(value, key);
        if (optional && !hasOwn) return;
        if (!hasOwn) {
          throw new SchemaValidationError(`Expected "${value}" to have key ${key}`);
        }
        ValidateAttributeValue(desc.objectRestrictions.values[key], value[key], path + "." + key, schemas);
      });
    }
  } else if (desc.dataType === "Array") {
    if (!Array.isArray(value)) {
      throw new SchemaValidationError(`Expected "${value}" to be of type array`);
    }
    value.forEach((entry) => {
      ValidateAttributeValue(desc.arrayRestrictions.value, entry, path + ".<array>.", schemas);
    });
  } else {
    throw new SchemaValidationError(`Unexpected datatype ${desc.dataType}`);
  }
}
function Validate(schemas, inputNodes) {
  inputNodes.forEach((node) => {
    Object.keys(node.attributes)
      .filter((v) => !v.startsWith("__internal"))
      .forEach((schemaID) => {
        if (!schemas[schemaID]) {
          throw new SchemaValidationError(`Missing schema "${schemaID}" referenced by ["${node.path}"].attributes`);
        }
        let schema = schemas[schemaID];
        let value = node.attributes[schemaID];
        try {
          ValidateAttributeValue(schema.value, value, "", schemas);
        } catch (e) {
          if (e instanceof SchemaValidationError) {
            throw new SchemaValidationError(
              `Error validating ["${node.path}"].attributes["${schemaID}"]: ${e.message}`
            );
          } else {
            throw e;
          }
        }
      });
  });
}
function ToInputNodes(data) {
  let inputNodes =  new Map();
  data.forEach((ifcxNode) => {
    let node = {
      path: ifcxNode.path,
      children: ifcxNode.children ? ifcxNode.children : {},
      inherits: ifcxNode.inherits ? ifcxNode.inherits : {},
      attributes: ifcxNode.attributes ? ifcxNode.attributes : {},
    };
    MMSet(inputNodes, node.path, node);
  });
  return inputNodes;
}
function LoadIfcxFile(file, checkSchemas = true, createArtificialRoot = true) {
  let inputNodes = ToInputNodes(file.data);
  let compositionNodes = FlattenCompositionInput(inputNodes);
  try {
    if (checkSchemas) {
      Validate(file.schemas, compositionNodes);
    }
  } catch (e) {
    throw e;
  }
  if (createArtificialRoot) {
    return CreateArtificialRoot(compositionNodes);
  } else {
    return ExpandFirstRootInInput(compositionNodes);
  }
}
function Federate(files) {
  if (files.length === 0) {
    throw new Error(`Trying to federate empty set of files`);
  }
  let result = {
    header: files[0].header,
    schemas: {},
    data: [],
  };
  files.forEach((file) => {
    Object.keys(file.schemas).forEach((schemaID) => (result.schemas[schemaID] = file.schemas[schemaID]));
  });
  files.forEach((file) => {
    file.data.forEach((node) => result.data.push(node));
  });
  return Prune(result);
}
function Collapse(nodes, deleteEmpty = false) {
  let result = {
    path: nodes[0].path,
    children: {},
    inherits: {},
    attributes: {},
  };
  nodes.forEach((node) => {
    Object.keys(node.children).forEach((name) => {
      result.children[name] = node.children[name];
    });
    Object.keys(node.inherits).forEach((name) => {
      result.inherits[name] = node.inherits[name];
    });
    Object.keys(node.attributes).forEach((name) => {
      result.attributes[name] = node.attributes[name];
    });
  });
  if (deleteEmpty) {
    let empty = true;
    Object.keys(result.children).forEach((name) => {
      if (result.children[name] !== null) empty = false;
    });
    Object.keys(result.inherits).forEach((name) => {
      if (result.inherits[name] !== null) empty = false;
    });
    Object.keys(result.attributes).forEach((name) => {
      if (result.attributes[name] !== null) empty = false;
    });
    if (empty) return null;
  }
  return result;
}
function Prune(file, deleteEmpty = false) {
  let result = {
    header: file.header,
    imports: [],
    schemas: file.schemas,
    data: [],
  };
  let inputNodes = ToInputNodes(file.data);
  inputNodes.forEach((nodes) => {
    let collapsed = Collapse(nodes, deleteEmpty);
    if (collapsed)
      result.data.push({
        path: collapsed.path,
        children: collapsed.children,
        inherits: collapsed.inherits,
        attributes: collapsed.attributes,
      });
  });
  return result;
}
var IfcxLayerStack = class {
  layers;
  tree;
  schemas;
  federated;
  constructor(layers) {
    this.layers = layers;
    this.Compose();
  }
  GetLayerIds() {
    return this.layers.map((l) => l.header.id);
  }
  Compose() {
    this.federated = Federate(this.layers);
    this.schemas = this.federated.schemas;
    this.tree = LoadIfcxFile(this.federated);
  }
  GetFullTree() {
    this.Compose();
    return this.tree;
  }
  GetFederatedLayer() {
    return this.federated;
  }
  GetSchemas() {
    return this.schemas;
  }
};
var IfcxLayerStackBuilder = class {
  provider;
  mainLayerId = null;
  constructor(provider) {
    this.provider = provider;
  }
  FromId(id) {
    this.mainLayerId = id;
    return this;
  }
  async Build() {
    if (!this.mainLayerId) throw new Error(`no main layer ID specified`);
    let layers = await this.BuildLayerSet(this.mainLayerId);
    if (layers instanceof Error) {
      return layers;
    }
    try {
      return new IfcxLayerStack(layers);
    } catch (e) {
      return e;
    }
  }
  async SatisfyDependencies(activeLayer, placed, orderedLayers) {
    let pending = [];
    for (const impt of activeLayer.imports) {
      if (!placed.has(impt.uri)) {
        let layer = await this.provider.GetLayerByURI(impt.uri);
        if (layer instanceof Error) {
          return layer;
        }
        pending.push(layer);
        placed.set(impt.uri, true);
      }
    }
    let temp = [];
    for (const layer of pending) {
      temp.push(layer);
      let layers = await this.SatisfyDependencies(layer, placed, orderedLayers);
      if (layers instanceof Error) {
        return layers;
      }
      temp.push(...layers);
    }
    temp.forEach((t) => orderedLayers.push(t));
    return temp;
  }
  async BuildLayerSet(activeLayerID) {
    let activeLayer = await this.provider.GetLayerByURI(activeLayerID);
    if (activeLayer instanceof Error) {
      return activeLayer;
    }
    let layerSet = [activeLayer];
    let placed =  new Map();
    placed.set(activeLayer.header.id, true);
    let result = await this.SatisfyDependencies(activeLayer, placed, layerSet);
    if (result instanceof Error) {
      return result;
    }
    return layerSet;
  }
};
function TreeNodeToComposedObject(path, node, schemas) {
  let co = {
    name: path,
    attributes: {},
    children: [],
  };
  node.children.forEach((childNode, childName) => {
    co.children?.push(TreeNodeToComposedObject(`${path}/${childName}`, childNode, schemas));
  });
  node.attributes.forEach((attr, attrName) => {
    if (attr && typeof attr === "object" && !Array.isArray(attr)) {
      Object.keys(attr).forEach((compname) => {
        co.attributes[`${attrName}::${compname}`] = attr[compname];
      });
    } else {
      let schema = schemas[attrName];
      if (schema && schema.value.quantityKind) {
        let postfix = "";
        let quantityKind = schema.value.quantityKind;
        if (quantityKind === "Length") {
          postfix = "m";
        } else if (quantityKind === "Volume") {
          postfix = "m" + String.fromCodePoint(179);
        }
        co.attributes[attrName] = `${attr} ${postfix}`;
      } else {
        co.attributes[attrName] = attr;
      }
    }
  });
  if (Object.keys(co.attributes).length === 0) delete co.attributes;
  return co;
}
async function compose3(files) {
  let userDefinedOrder = {
    header: { ...files[0].header },
    imports: files.map((f) => {
      return { uri: f.header.id };
    }),
    schemas: {},
    data: [],
  };
  userDefinedOrder.header.id = "USER_DEF";
  let provider = new StackedLayerProvider([
    new InMemoryLayerProvider().AddAll([userDefinedOrder, ...files]),
    new FetchLayerProvider(),
  ]);
  let layerStack = await new IfcxLayerStackBuilder(provider).FromId(userDefinedOrder.header.id).Build();
  if (layerStack instanceof Error) {
    throw layerStack;
  }
  layerStack.GetFederatedLayer().data.forEach((n, i) => {
    n.attributes = n.attributes || {};
    n.attributes[`__internal_${i}`] = n.path;
  });
  return TreeNodeToComposedObject("", layerStack.GetFullTree(), layerStack.GetSchemas());
}
var scene;
var camera;
var datas = [];
var autoCamera = true;
var objectMap = {};
var primMap = {};
var envMap;
function init() {
  scene = new THREE.Scene();
  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100);
  camera.up.set(0, 0, 1);
  camera.position.set(50, 50, 50);
  camera.lookAt(0, 0, 0);
  scene.add(camera);
  return scene;
}
function HasAttr(node, attrName) {
  if (!node || !node.attributes) return false;
  return !!node.attributes[attrName];
}
function tryCreateMeshGltfMaterial(path) {
  for (let p of path) {
    if (!p.attributes) {
      continue;
    }
    const pbrMetallicRoughness = p.attributes["gltf::material::pbrMetallicRoughness"];
    const normalTexture = p.attributes["gltf::material::normalTexture"];
    const occlusionTexture = p.attributes["gltf::material::occlusionTexture"];
    const emissiveTexture = p.attributes["gltf::material::emissiveTexture"];
    const emissiveFactor = p.attributes["gltf::material::emissiveFactor"];
    const alphaMode = p.attributes["gltf::material::alphaMode"];
    const alphaCutoff = p.attributes["gltf::material::alphaCutoff"];
    const doubleSided = p.attributes["gltf::material::doubleSided"];
    if (
      !pbrMetallicRoughness &&
      !normalTexture &&
      !occlusionTexture &&
      !emissiveTexture &&
      !emissiveFactor &&
      !alphaMode &&
      !alphaCutoff &&
      !doubleSided
    ) {
      continue;
    }
    let material = new THREE.MeshStandardMaterial();
    material.color = new THREE.Color(1, 1, 1);
    material.metalness = 1;
    material.roughness = 1;
    if (pbrMetallicRoughness) {
      let baseColorFactor = pbrMetallicRoughness["baseColorFactor"];
      if (baseColorFactor) {
        material.color = new THREE.Color(baseColorFactor[0], baseColorFactor[1], baseColorFactor[2]);
      }
      let metallicFactor = pbrMetallicRoughness["metallicFactor"];
      if (metallicFactor !== void 0) {
        material.metalness = metallicFactor;
      }
      let roughnessFactor = pbrMetallicRoughness["roughnessFactor"];
      if (roughnessFactor !== void 0) {
        material.roughness = roughnessFactor;
      }
    }
    material.envMap = envMap;
    material.needsUpdate = true;
    material.envMapRotation = new THREE.Euler(0.5 * Math.PI, 0, 0);
    return material;
  }
  return void 0;
}
function createMaterialFromParent(path) {
  let material = {
    color: new THREE.Color(0.6, 0.6, 0.6),
    transparent: false,
    opacity: 1,
  };
  for (let p of path) {
    const color = p.attributes ? p.attributes["bsi::ifc::presentation::diffuseColor"] : null;
    if (color) {
      material.color = new THREE.Color(...color);
      const opacity = p.attributes["bsi::ifc::presentation::opacity"];
      if (opacity) {
        material.transparent = true;
        material.opacity = opacity;
      }
      break;
    }
  }
  return material;
}
function createCurveFromJson(path) {
  let points = new Float32Array(path[0].attributes["usd::usdgeom::basiscurves::points"].flat());
  const geometry = new THREE.BufferGeometry();
  geometry.setAttribute("position", new THREE.BufferAttribute(points, 3));
  const material = createMaterialFromParent(path);
  let lineMaterial = new THREE.LineBasicMaterial({ ...material });
  lineMaterial.color.multiplyScalar(0.8);
  return new THREE.Line(geometry, lineMaterial);
}
function createMeshFromJson(path) {
  let points = new Float32Array(path[0].attributes["usd::usdgeom::mesh::points"].flat());
  let indices = new Uint16Array(path[0].attributes["usd::usdgeom::mesh::faceVertexIndices"]);
  const geometry = new THREE.BufferGeometry();
  geometry.setAttribute("position", new THREE.BufferAttribute(points, 3));
  geometry.setIndex(new THREE.BufferAttribute(indices, 1));
  geometry.computeVertexNormals();
  var meshMaterial;
  let gltfPbrMaterial = tryCreateMeshGltfMaterial(path);
  if (gltfPbrMaterial) {
    meshMaterial = gltfPbrMaterial;
  } else {
    const m = createMaterialFromParent(path);
    meshMaterial = new THREE.MeshLambertMaterial({ ...m });
  }
  return new THREE.Mesh(geometry, meshMaterial);
}
function createPointsFromJsonPcdBase64(path) {
  const base64_string = path[0].attributes["pcd::base64"];
  const decoded = atob(base64_string);
  const len = decoded.length;
  const bytes = new Uint8Array(len);
  for (let i = 0; i < len; i++) {
    bytes[i] = decoded.charCodeAt(i);
  }
  const loader = new PCDLoader();
  const points = loader.parse(bytes.buffer);
  points.material.sizeAttenuation = false;
  points.material.size = 2;
  return points;
}
function createPoints(geometry, withColors) {
  const material = new THREE.PointsMaterial();
  material.sizeAttenuation = false;
  material.fog = true;
  material.size = 5;
  material.color = new THREE.Color(withColors ? 16777215 : 0);
  if (withColors) {
    material.vertexColors = true;
  }
  return new THREE.Points(geometry, material);
}
function createPointsFromJsonArray(path) {
  const geometry = new THREE.BufferGeometry();
  const positions = new Float32Array(path[0].attributes["points::array::positions"].flat());
  geometry.setAttribute("position", new THREE.Float32BufferAttribute(positions, 3));
  const colors = path[0].attributes["points::array::colors"];
  if (colors) {
    const colors_ = new Float32Array(colors.flat());
    geometry.setAttribute("color", new THREE.Float32BufferAttribute(colors_, 3));
  }
  return createPoints(geometry, colors);
}
function base64ToArrayBuffer(str) {
  let binary;
  try {
    binary = atob(str);
  } catch (e) {
    throw new Error("base64 encoded string is invalid");
  }
  const bytes = new Uint8Array(binary.length);
  for (let i = 0; i < binary.length; ++i) {
    bytes[i] = binary.charCodeAt(i);
  }
  return bytes.buffer;
}
function createPointsFromJsonPositionBase64(path) {
  const geometry = new THREE.BufferGeometry();
  const positions_base64 = path[0].attributes["points::base64::positions"];
  const positions_bytes = base64ToArrayBuffer(positions_base64);
  if (!positions_bytes) {
    return null;
  }
  const positions = new Float32Array(positions_bytes);
  geometry.setAttribute("position", new THREE.Float32BufferAttribute(positions, 3));
  const colors_base64 = path[0].attributes["points::base64::colors"];
  if (colors_base64) {
    const colors_bytes = base64ToArrayBuffer(colors_base64);
    if (colors_bytes) {
      const colors = new Float32Array(colors_bytes);
      geometry.setAttribute("color", new THREE.Float32BufferAttribute(colors, 3));
    }
  }
  return createPoints(geometry, colors_base64);
}
function traverseTree(path, parent, pathMapping) {
  const node = path[0];
  let elem = new THREE.Group();
  if (HasAttr(node, "usd::usdgeom::visibility::visibility")) {
    if (node.attributes["usd::usdgeom::visibility::visibility"] === "invisible") {
      return;
    }
  } else if (HasAttr(node, "usd::usdgeom::mesh::points")) {
    elem = createMeshFromJson(path);
  } else if (HasAttr(node, "usd::usdgeom::basiscurves::points")) {
    elem = createCurveFromJson(path);
  } else if (HasAttr(node, "pcd::base64")) {
    elem = createPointsFromJsonPcdBase64(path);
  } else if (HasAttr(node, "points::array::positions")) {
    elem = createPointsFromJsonArray(path);
  } else if (HasAttr(node, "points::base64::positions")) {
    elem = createPointsFromJsonPositionBase64(path);
  }
  objectMap[node.name] = elem;
  primMap[node.name] = node;
  elem.userData.path = node.name;
  for (let path2 of Object.entries(node.attributes || {})
    .filter(([k, _]) => k.startsWith("__internal_"))
    .map(([_, v]) => v)) {
    (pathMapping[String(path2)] = pathMapping[String(path2)] || []).push(node.name);
  }
  parent.add(elem);
  if (path.length > 1) {
    elem.matrixAutoUpdate = false;
    let matrixNode =
      node.attributes && node.attributes["usd::xformop::transform"]
        ? node.attributes["usd::xformop::transform"].flat()
        : null;
    if (matrixNode) {
      let matrix = new THREE.Matrix4();
      matrix.set(...matrixNode);
      matrix.transpose();
      elem.matrix = matrix;
    }
  }
  (node.children || []).forEach((child) => traverseTree([child, ...path], elem || parent, pathMapping));
}
async function composeAndRender() {
  if (scene) {
    scene.children = [];
  }
  objectMap = {};
  primMap = {};
  if (datas.length === 0) {
    return;
  }
  let tree = null;
  let dataArray = datas.map((arr) => arr[1]);
  tree = await compose3(dataArray);
  if (!tree) {
    console.error("No result from composition");
    return;
  }
  if (!scene) {
    init();
  }
  let pathMapping = {};
  traverseTree([tree], scene, pathMapping);
  if (autoCamera) {
    const boundingBox = new THREE.Box3();
    boundingBox.setFromObject(scene);
    if (!boundingBox.isEmpty()) {
      let avg = boundingBox.min.clone().add(boundingBox.max).multiplyScalar(0.5);
      let ext = boundingBox.max.clone().sub(boundingBox.min).length();
      camera.position.copy(avg.clone().add(new THREE.Vector3(1, 1, 1).normalize().multiplyScalar(ext)));
      camera.far = ext * 3;
      camera.updateProjectionMatrix();
      autoCamera = false;
    }
  }
}
async function parse(m, name) {
  datas.push([name, m]);
  await composeAndRender();
  return scene;
}
function clear() {
  scene = undefined;
  datas.length = 0;
  autoCamera = true;
}

class IFCXLoader extends Loader {
    load(url, onLoad, onProgress, onError) {
        const manager = this.manager;
        manager.itemStart(url);
        const _onLoad = (scene) => {
            onLoad(scene);
            manager.itemEnd(url);
        };
        const _onError = (e) => {
            if (onError)
                onError(e);
            else
                console.error(e);
            manager.itemError(url);
            manager.itemEnd(url);
        };
        const loader = new FileLoader(this.manager);
        loader.setPath(this.path);
        loader.setResponseType("json");
        loader.setRequestHeader(this.requestHeader);
        loader.setWithCredentials(this.withCredentials);
        loader.load(url, (json) => this.parse(json, _onLoad, _onError), onProgress, onError);
    }
    parse(json, onLoad, onError) {
        parse(json)
            .then((scene) => onLoad(scene))
            .catch((err) => onError(err))
            .finally(() => clear());
    }
}

class IFCXFileLoader extends Loader$1 {
    constructor(viewer) {
        super();
        this.viewer = viewer;
        this.manager = null;
    }
    dispose() {
        if (this.manager)
            this.manager.dispose();
    }
    isSupport(file, format = "") {
        return ((typeof file === "string" || file instanceof globalThis.File || file instanceof ArrayBuffer) &&
            /(ifcx)$/i.test(format));
    }
    async load(file, format, params = {}) {
        this.manager = new GLTFLoadingManager(file, params);
        const loader = new IFCXLoader(this.manager);
        loader.setPath(this.manager.path);
        loader.setCrossOrigin(params.crossOrigin || loader.crossOrigin);
        loader.setWithCredentials(params.withCredentials || loader.withCredentials);
        const progress = (event) => {
            const { lengthComputable, loaded, total } = event;
            const progress = lengthComputable ? loaded / total : 1;
            this.viewer.emitEvent({ type: "geometryprogress", data: progress, file });
        };
        const scene = await loader.loadAsync(this.manager.fileURL, progress);
        if (!this.viewer.scene)
            return this;
        let handle = 1;
        scene.traverse((object) => {
            object.userData = { handle: handle + "", ...object.userData };
            handle++;
        });
        const modelImpl = new ModelImpl(scene);
        modelImpl.id = params.modelId || this.extractFileName(file);
        this.viewer.scene.add(scene);
        this.viewer.models.push(modelImpl);
        this.viewer.syncOptions();
        this.viewer.syncOverlay();
        this.viewer.emitEvent({ type: "databasechunk", data: scene, file });
        this.viewer.update(true);
        return this;
    }
}

class IFCXCloudLoader extends Loader$1 {
    constructor(viewer) {
        super();
        this.viewer = viewer;
    }
    isSupport(file) {
        return (typeof file === "object" &&
            typeof file.type === "string" &&
            typeof file.download === "function" &&
            /.ifcx$/i.test(file.type));
    }
    async load(file) {
        const progress = (progress) => {
            this.viewer.emitEvent({ type: "geometryprogress", data: progress, file });
        };
        const arrayBuffer = await file.download(progress, this.abortController.signal);
        if (!this.viewer.scene)
            return this;
        const textDecoder = new TextDecoder();
        const json = JSON.parse(textDecoder.decode(arrayBuffer));
        const scene = await parse(json);
        clear();
        let handle = 1;
        scene.traverse((object) => {
            object.userData = { handle: handle + "", ...object.userData };
            handle++;
        });
        const modelImpl = new ModelImpl(scene);
        modelImpl.id = file.id;
        this.viewer.scene.add(scene);
        this.viewer.models.push(modelImpl);
        this.viewer.syncOptions();
        this.viewer.syncOverlay();
        this.viewer.emitEvent({ type: "databasechunk", data: scene, file });
        this.viewer.update(true);
        return this;
    }
}

loaders.registerLoader("ifcx-file", (viewer) => new IFCXFileLoader(viewer));
loaders.registerLoader("ifcx-cloud", (viewer) => new IFCXCloudLoader(viewer));
//# sourceMappingURL=IFCXLoader.module.js.map
