@inweb/viewer-three 26.7.6 → 26.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29,6 +29,7 @@
29
29
  // (C) buildingSMART International
30
30
  // published under MIT license
31
31
 
32
+ /* eslint-disable lines-between-class-members */
32
33
  /* eslint-disable @typescript-eslint/no-unused-vars */
33
34
  /* eslint-disable prefer-const */
34
35
  /* eslint-disable no-useless-catch */
@@ -38,12 +39,18 @@ import {
38
39
  BufferAttribute,
39
40
  BufferGeometry,
40
41
  Color,
42
+ Euler,
43
+ Float32BufferAttribute,
41
44
  Group,
42
45
  Line,
43
46
  LineBasicMaterial,
44
47
  Matrix4,
45
48
  Mesh,
46
49
  MeshBasicMaterial,
50
+ MeshLambertMaterial,
51
+ MeshStandardMaterial,
52
+ Points,
53
+ PointsMaterial,
47
54
  PerspectiveCamera,
48
55
  Scene,
49
56
  Vector3,
@@ -54,88 +61,107 @@ const THREE = {
54
61
  BufferAttribute,
55
62
  BufferGeometry,
56
63
  Color,
64
+ Euler,
65
+ Float32BufferAttribute,
57
66
  Group,
58
67
  Line,
59
68
  LineBasicMaterial,
60
69
  Matrix4,
61
70
  Mesh,
62
71
  MeshBasicMaterial,
72
+ MeshLambertMaterial,
73
+ MeshStandardMaterial,
74
+ Points,
75
+ PointsMaterial,
63
76
  PerspectiveCamera,
64
77
  Scene,
65
78
  Vector3,
66
79
  };
67
80
 
68
- // composed-object.ts
69
- function getChildByName(root, childName, skip = 0) {
70
- let fragments = childName.replace(/^<\/|^\/|>$/g, "").split("/");
71
- for (let i = 0; i < skip; ++i) {
72
- fragments.shift();
81
+ // ifcx-core/layers/layer-providers.ts
82
+ var StackedLayerProvider = class {
83
+ providers;
84
+ constructor(providers) {
85
+ this.providers = providers;
73
86
  }
74
- let start = root;
75
- while (fragments.length && start && start.children) {
76
- // console.log(start, fragments[0]);
77
- let f = fragments.shift();
78
- start = start.children.find((i) => i.name.split("/").reverse()[0] === f);
87
+ async GetLayerByURI(uri) {
88
+ let errorStack = [];
89
+ for (let provider of this.providers) {
90
+ let layer = await provider.GetLayerByURI(uri);
91
+ if (!(layer instanceof Error)) {
92
+ return layer;
93
+ } else {
94
+ errorStack.push(layer);
95
+ }
96
+ }
97
+ return new Error(JSON.stringify(errorStack));
98
+ }
99
+ };
100
+ var InMemoryLayerProvider = class {
101
+ layers;
102
+ constructor() {
103
+ this.layers = /* @__PURE__ */ new Map();
104
+ }
105
+ GetLayerByURI(uri) {
106
+ if (!this.layers.has(uri)) {
107
+ return new Error(`File with uri "${uri}" not found`);
108
+ }
109
+ return Promise.resolve(this.layers.get(uri));
79
110
  }
80
- if (fragments.length == 0) {
81
- return start;
111
+ add(file) {
112
+ if (this.layers.has(file.header.id)) {
113
+ throw new Error(`Inserting file with duplicate ID "${file.header.id}"`);
114
+ }
115
+ this.layers.set(file.header.id, file);
116
+ return this;
117
+ }
118
+ AddAll(files) {
119
+ files.forEach((f) => this.add(f));
120
+ return this;
121
+ }
122
+ };
123
+
124
+ // ifcx-core/util/log.ts
125
+ var LOG_ENABLED = true;
126
+ function log(bla) {
127
+ if (LOG_ENABLED) {
128
+ console.log(`${JSON.stringify(arguments)}`);
82
129
  }
83
130
  }
84
131
 
85
- // compose-alpha.ts
86
- function GetNode(node, path) {
87
- if (path === "") return node;
88
- let parts = path.split("/");
89
- let child = node.children.get(parts[0]);
90
- if (child) {
91
- if (parts.length === 1) {
92
- return child;
132
+ // ifcx-core/layers/fetch-layer-provider.ts
133
+ var FetchLayerProvider = class {
134
+ layers;
135
+ constructor() {
136
+ this.layers = /* @__PURE__ */ new Map();
137
+ }
138
+ async FetchJson(url) {
139
+ let result = await fetch(url);
140
+ if (!result.ok) {
141
+ return new Error(`Failed to fetch ${url}: ${result.status}`);
142
+ }
143
+ try {
144
+ return await result.json();
145
+ } catch (e) {
146
+ log(url);
147
+ return new Error(`Failed to parse json at ${url}: ${e}`);
93
148
  }
94
- return GetNode(child, GetTail(path));
95
- } else {
96
- return null;
97
149
  }
98
- }
99
- function GetHead(path) {
100
- return path.split("/")[0];
101
- }
102
- function GetTail(path) {
103
- let parts = path.split("/");
104
- parts.shift();
105
- return parts.join("/");
106
- }
107
- function MakeNode(node) {
108
- return {
109
- node,
110
- children: /* @__PURE__ */ new Map(),
111
- attributes: /* @__PURE__ */ new Map(),
112
- };
113
- }
114
- function ConvertToCompositionNode(path, inputNodes) {
115
- let compositionNode = {
116
- path,
117
- children: {},
118
- inherits: {},
119
- attributes: {},
120
- };
121
- inputNodes.forEach((node) => {
122
- Object.keys(node.children).forEach((childName) => {
123
- compositionNode.children[childName] = node.children[childName];
124
- });
125
- Object.keys(node.inherits).forEach((inheritName) => {
126
- let ih = node.inherits[inheritName];
127
- if (ih === null) {
128
- delete compositionNode.inherits[inheritName];
129
- } else {
130
- compositionNode.inherits[inheritName] = ih;
150
+ async GetLayerByURI(uri) {
151
+ if (!this.layers.has(uri)) {
152
+ let fetched = await this.FetchJson(uri);
153
+ if (fetched instanceof Error) {
154
+ return new Error(`File with id "${uri}" not found`);
131
155
  }
132
- });
133
- Object.keys(node.attributes).forEach((attrName) => {
134
- compositionNode.attributes[attrName] = node.attributes[attrName];
135
- });
136
- });
137
- return compositionNode;
138
- }
156
+ let file = fetched;
157
+ this.layers.set(uri, file);
158
+ return file;
159
+ }
160
+ return this.layers.get(uri);
161
+ }
162
+ };
163
+
164
+ // ifcx-core/util/mm.ts
139
165
  function MMSet(map, key, value) {
140
166
  if (map.has(key)) {
141
167
  map.get(key)?.push(value);
@@ -143,6 +169,9 @@ function MMSet(map, key, value) {
143
169
  map.set(key, [value]);
144
170
  }
145
171
  }
172
+
173
+ // ifcx-core/composition/cycles.ts
174
+ var CycleError = class extends Error {};
146
175
  function FindRootsOrCycles(nodes) {
147
176
  let dependencies = /* @__PURE__ */ new Map();
148
177
  let dependents = /* @__PURE__ */ new Map();
@@ -182,20 +211,78 @@ function FindRootsOrCycles(nodes) {
182
211
  }
183
212
  return roots;
184
213
  }
185
- function ConvertNodes(input) {
214
+
215
+ // ifcx-core/composition/path.ts
216
+ function GetHead(path) {
217
+ return path.split("/")[0];
218
+ }
219
+ function GetTail(path) {
220
+ let parts = path.split("/");
221
+ parts.shift();
222
+ return parts.join("/");
223
+ }
224
+
225
+ // ifcx-core/composition/node.ts
226
+ function MakePostCompositionNode(node) {
227
+ return {
228
+ node,
229
+ children: /* @__PURE__ */ new Map(),
230
+ attributes: /* @__PURE__ */ new Map(),
231
+ };
232
+ }
233
+ function GetChildNodeWithPath(node, path) {
234
+ if (path === "") return node;
235
+ let parts = path.split("/");
236
+ let child = node.children.get(parts[0]);
237
+ if (child) {
238
+ if (parts.length === 1) {
239
+ return child;
240
+ }
241
+ return GetChildNodeWithPath(child, GetTail(path));
242
+ } else {
243
+ return null;
244
+ }
245
+ }
246
+
247
+ // ifcx-core/composition/compose.ts
248
+ function FlattenPathToPreCompositionNode(path, inputNodes) {
249
+ let compositionNode = {
250
+ path,
251
+ children: {},
252
+ inherits: {},
253
+ attributes: {},
254
+ };
255
+ inputNodes.forEach((node) => {
256
+ Object.keys(node.children).forEach((childName) => {
257
+ compositionNode.children[childName] = node.children[childName];
258
+ });
259
+ Object.keys(node.inherits).forEach((inheritName) => {
260
+ let ih = node.inherits[inheritName];
261
+ if (ih === null) {
262
+ delete compositionNode.inherits[inheritName];
263
+ } else {
264
+ compositionNode.inherits[inheritName] = ih;
265
+ }
266
+ });
267
+ Object.keys(node.attributes).forEach((attrName) => {
268
+ compositionNode.attributes[attrName] = node.attributes[attrName];
269
+ });
270
+ });
271
+ return compositionNode;
272
+ }
273
+ function FlattenCompositionInput(input) {
186
274
  let compositionNodes = /* @__PURE__ */ new Map();
187
275
  for (let [path, inputNodes] of input) {
188
- compositionNodes.set(path, ConvertToCompositionNode(path, inputNodes));
276
+ compositionNodes.set(path, FlattenPathToPreCompositionNode(path, inputNodes));
189
277
  }
190
278
  return compositionNodes;
191
279
  }
192
- var CycleError = class extends Error {};
193
280
  function ExpandFirstRootInInput(nodes) {
194
281
  let roots = FindRootsOrCycles(nodes);
195
282
  if (!roots) {
196
283
  throw new CycleError();
197
284
  }
198
- return ExpandNewNode([...roots.values()][0], nodes);
285
+ return ComposeNodeFromPath([...roots.values()][0], nodes);
199
286
  }
200
287
  function CreateArtificialRoot(nodes) {
201
288
  let roots = FindRootsOrCycles(nodes);
@@ -208,28 +295,28 @@ function CreateArtificialRoot(nodes) {
208
295
  children: /* @__PURE__ */ new Map(),
209
296
  };
210
297
  roots.forEach((root) => {
211
- pseudoRoot.children.set(root, ExpandNewNode(root, nodes));
298
+ pseudoRoot.children.set(root, ComposeNodeFromPath(root, nodes));
212
299
  });
213
300
  return pseudoRoot;
214
301
  }
215
- function ExpandNewNode(node, nodes) {
216
- return ExpandNode(node, MakeNode(node), nodes);
302
+ function ComposeNodeFromPath(path, preCompositionNodes) {
303
+ return ComposeNode(path, MakePostCompositionNode(path), preCompositionNodes);
217
304
  }
218
- function ExpandNode(path, node, nodes) {
219
- let input = nodes.get(path);
220
- if (input) {
221
- AddDataFromInput(input, node, nodes);
305
+ function ComposeNode(path, postCompositionNode, preCompositionNodes) {
306
+ let preCompositionNode = preCompositionNodes.get(path);
307
+ if (preCompositionNode) {
308
+ AddDataFromPreComposition(preCompositionNode, postCompositionNode, preCompositionNodes);
222
309
  }
223
- node.children.forEach((child, name) => {
224
- ExpandNode(`${path}/${name}`, child, nodes);
310
+ postCompositionNode.children.forEach((child, name) => {
311
+ ComposeNode(`${path}/${name}`, child, preCompositionNodes);
225
312
  });
226
- return node;
313
+ return postCompositionNode;
227
314
  }
228
- function AddDataFromInput(input, node, nodes) {
229
- Object.values(input.inherits).forEach((inherit) => {
230
- let classNode = ExpandNewNode(GetHead(inherit), nodes);
231
- let subnode = GetNode(classNode, GetTail(inherit));
232
- if (!subnode) throw new Error(`Unknown node ${inherit}`);
315
+ function AddDataFromPreComposition(input, node, nodes) {
316
+ Object.values(input.inherits).forEach((inheritPath) => {
317
+ let classNode = ComposeNodeFromPath(GetHead(inheritPath), nodes);
318
+ let subnode = GetChildNodeWithPath(classNode, GetTail(inheritPath));
319
+ if (!subnode) throw new Error(`Unknown node ${inheritPath}`);
233
320
  subnode.children.forEach((child, childName) => {
234
321
  node.children.set(childName, child);
235
322
  });
@@ -239,8 +326,8 @@ function AddDataFromInput(input, node, nodes) {
239
326
  });
240
327
  Object.entries(input.children).forEach(([childName, child]) => {
241
328
  if (child !== null) {
242
- let classNode = ExpandNewNode(GetHead(child), nodes);
243
- let subnode = GetNode(classNode, GetTail(child));
329
+ let classNode = ComposeNodeFromPath(GetHead(child), nodes);
330
+ let subnode = GetChildNodeWithPath(classNode, GetTail(child));
244
331
  if (!subnode) throw new Error(`Unknown node ${child}`);
245
332
  node.children.set(childName, subnode);
246
333
  } else {
@@ -252,29 +339,12 @@ function AddDataFromInput(input, node, nodes) {
252
339
  });
253
340
  }
254
341
 
255
- // workflow-alpha.ts
256
- function MMSet2(map, key, value) {
257
- if (map.has(key)) {
258
- map.get(key)?.push(value);
259
- } else {
260
- map.set(key, [value]);
261
- }
262
- }
263
- function ToInputNodes(data) {
264
- let inputNodes = /* @__PURE__ */ new Map();
265
- data.forEach((ifcxNode) => {
266
- let node = {
267
- path: ifcxNode.path,
268
- children: ifcxNode.children ? ifcxNode.children : {},
269
- inherits: ifcxNode.inherits ? ifcxNode.inherits : {},
270
- attributes: ifcxNode.attributes ? ifcxNode.attributes : {},
271
- };
272
- MMSet2(inputNodes, node.path, node);
273
- });
274
- return inputNodes;
275
- }
342
+ // ifcx-core/schema/schema-validation.ts
276
343
  var SchemaValidationError = class extends Error {};
277
344
  function ValidateAttributeValue(desc, value, path, schemas) {
345
+ if (desc.optional && value === void 0) {
346
+ return;
347
+ }
278
348
  if (desc.inherits) {
279
349
  desc.inherits.forEach((inheritedSchemaID) => {
280
350
  let inheritedSchema = schemas[inheritedSchemaID];
@@ -312,7 +382,7 @@ function ValidateAttributeValue(desc, value, path, schemas) {
312
382
  if (typeof value !== "number") {
313
383
  throw new SchemaValidationError(`Expected "${value}" to be of type real`);
314
384
  }
315
- } else if (desc.dataType === "Relation") {
385
+ } else if (desc.dataType === "Reference") {
316
386
  if (typeof value !== "string") {
317
387
  throw new SchemaValidationError(`Expected "${value}" to be of type string`);
318
388
  }
@@ -322,7 +392,10 @@ function ValidateAttributeValue(desc, value, path, schemas) {
322
392
  }
323
393
  if (desc.objectRestrictions) {
324
394
  Object.keys(desc.objectRestrictions.values).forEach((key) => {
325
- if (!Object.hasOwn(value, key)) {
395
+ let optional = desc.objectRestrictions.values[key].optional;
396
+ let hasOwn = Object.hasOwn(value, key);
397
+ if (optional && !hasOwn) return;
398
+ if (!hasOwn) {
326
399
  throw new SchemaValidationError(`Expected "${value}" to have key ${key}`);
327
400
  }
328
401
  ValidateAttributeValue(desc.objectRestrictions.values[key], value[key], path + "." + key, schemas);
@@ -341,27 +414,46 @@ function ValidateAttributeValue(desc, value, path, schemas) {
341
414
  }
342
415
  function Validate(schemas, inputNodes) {
343
416
  inputNodes.forEach((node) => {
344
- Object.keys(node.attributes).forEach((schemaID) => {
345
- if (!schemas[schemaID]) {
346
- throw new SchemaValidationError(`Missing schema "${schemaID}" referenced by ["${node.path}"].attributes`);
347
- }
348
- let schema = schemas[schemaID];
349
- let value = node.attributes[schemaID];
350
- try {
351
- ValidateAttributeValue(schema.value, value, "", schemas);
352
- } catch (e) {
353
- if (e instanceof SchemaValidationError) {
354
- throw new SchemaValidationError(`Error validating ["${node.path}"].attributes["${schemaID}"]: ${e.message}`);
355
- } else {
356
- throw e;
417
+ Object.keys(node.attributes)
418
+ .filter((v) => !v.startsWith("__internal"))
419
+ .forEach((schemaID) => {
420
+ if (!schemas[schemaID]) {
421
+ throw new SchemaValidationError(`Missing schema "${schemaID}" referenced by ["${node.path}"].attributes`);
357
422
  }
358
- }
359
- });
423
+ let schema = schemas[schemaID];
424
+ let value = node.attributes[schemaID];
425
+ try {
426
+ ValidateAttributeValue(schema.value, value, "", schemas);
427
+ } catch (e) {
428
+ if (e instanceof SchemaValidationError) {
429
+ throw new SchemaValidationError(
430
+ `Error validating ["${node.path}"].attributes["${schemaID}"]: ${e.message}`
431
+ );
432
+ } else {
433
+ throw e;
434
+ }
435
+ }
436
+ });
437
+ });
438
+ }
439
+
440
+ // ifcx-core/workflows.ts
441
+ function ToInputNodes(data) {
442
+ let inputNodes = /* @__PURE__ */ new Map();
443
+ data.forEach((ifcxNode) => {
444
+ let node = {
445
+ path: ifcxNode.path,
446
+ children: ifcxNode.children ? ifcxNode.children : {},
447
+ inherits: ifcxNode.inherits ? ifcxNode.inherits : {},
448
+ attributes: ifcxNode.attributes ? ifcxNode.attributes : {},
449
+ };
450
+ MMSet(inputNodes, node.path, node);
360
451
  });
452
+ return inputNodes;
361
453
  }
362
- function LoadIfcxFile(file, checkSchemas = true, createArtificialRoot = false) {
454
+ function LoadIfcxFile(file, checkSchemas = true, createArtificialRoot = true) {
363
455
  let inputNodes = ToInputNodes(file.data);
364
- let compositionNodes = ConvertNodes(inputNodes);
456
+ let compositionNodes = FlattenCompositionInput(inputNodes);
365
457
  try {
366
458
  if (checkSchemas) {
367
459
  Validate(file.schemas, compositionNodes);
@@ -376,8 +468,12 @@ function LoadIfcxFile(file, checkSchemas = true, createArtificialRoot = false) {
376
468
  }
377
469
  }
378
470
  function Federate(files) {
471
+ if (files.length === 0) {
472
+ throw new Error(`Trying to federate empty set of files`);
473
+ }
379
474
  let result = {
380
475
  header: files[0].header,
476
+ imports: [],
381
477
  schemas: {},
382
478
  data: [],
383
479
  };
@@ -425,6 +521,7 @@ function Collapse(nodes, deleteEmpty = false) {
425
521
  function Prune(file, deleteEmpty = false) {
426
522
  let result = {
427
523
  header: file.header,
524
+ imports: [],
428
525
  schemas: file.schemas,
429
526
  data: [],
430
527
  };
@@ -442,7 +539,99 @@ function Prune(file, deleteEmpty = false) {
442
539
  return result;
443
540
  }
444
541
 
445
- // compose-flattened.ts
542
+ // ifcx-core/layers/layer-stack.ts
543
+ var IfcxLayerStack = class {
544
+ // main layer at 0
545
+ layers;
546
+ tree;
547
+ schemas;
548
+ federated;
549
+ constructor(layers) {
550
+ this.layers = layers;
551
+ this.Compose();
552
+ }
553
+ GetLayerIds() {
554
+ return this.layers.map((l) => l.header.id);
555
+ }
556
+ Compose() {
557
+ this.federated = Federate(this.layers);
558
+ this.schemas = this.federated.schemas;
559
+ this.tree = LoadIfcxFile(this.federated);
560
+ }
561
+ GetFullTree() {
562
+ this.Compose();
563
+ return this.tree;
564
+ }
565
+ GetFederatedLayer() {
566
+ return this.federated;
567
+ }
568
+ GetSchemas() {
569
+ return this.schemas;
570
+ }
571
+ };
572
+ var IfcxLayerStackBuilder = class {
573
+ provider;
574
+ mainLayerId = null;
575
+ constructor(provider) {
576
+ this.provider = provider;
577
+ }
578
+ FromId(id) {
579
+ this.mainLayerId = id;
580
+ return this;
581
+ }
582
+ async Build() {
583
+ if (!this.mainLayerId) throw new Error(`no main layer ID specified`);
584
+ let layers = await this.BuildLayerSet(this.mainLayerId);
585
+ if (layers instanceof Error) {
586
+ return layers;
587
+ }
588
+ try {
589
+ return new IfcxLayerStack(layers);
590
+ } catch (e) {
591
+ return e;
592
+ }
593
+ }
594
+ async SatisfyDependencies(activeLayer, placed, orderedLayers) {
595
+ let pending = [];
596
+ for (const impt of activeLayer.imports) {
597
+ if (!placed.has(impt.uri)) {
598
+ let layer = await this.provider.GetLayerByURI(impt.uri);
599
+ if (layer instanceof Error) {
600
+ return layer;
601
+ }
602
+ pending.push(layer);
603
+ placed.set(impt.uri, true);
604
+ }
605
+ }
606
+ let temp = [];
607
+ for (const layer of pending) {
608
+ temp.push(layer);
609
+ let layers = await this.SatisfyDependencies(layer, placed, orderedLayers);
610
+ if (layers instanceof Error) {
611
+ return layers;
612
+ }
613
+ temp.push(...layers);
614
+ }
615
+ temp.forEach((t) => orderedLayers.push(t));
616
+ return temp;
617
+ }
618
+ async BuildLayerSet(activeLayerID) {
619
+ let activeLayer = await this.provider.GetLayerByURI(activeLayerID);
620
+ if (activeLayer instanceof Error) {
621
+ return activeLayer;
622
+ }
623
+ let layerSet = [activeLayer];
624
+ let placed = /* @__PURE__ */ new Map();
625
+ placed.set(activeLayer.header.id, true);
626
+ let result = await this.SatisfyDependencies(activeLayer, placed, layerSet);
627
+ if (result instanceof Error) {
628
+ return result;
629
+ }
630
+ return layerSet;
631
+ }
632
+ };
633
+
634
+ // viewer/compose-flattened.ts
446
635
  function TreeNodeToComposedObject(path, node, schemas) {
447
636
  let co = {
448
637
  name: path,
@@ -476,105 +665,345 @@ function TreeNodeToComposedObject(path, node, schemas) {
476
665
  if (Object.keys(co.attributes).length === 0) delete co.attributes;
477
666
  return co;
478
667
  }
479
- function compose3(files) {
480
- let federated = Federate(files);
481
- let tree = LoadIfcxFile(federated, true, true);
482
- return TreeNodeToComposedObject("", tree, federated.schemas);
668
+ async function compose3(files) {
669
+ let userDefinedOrder = {
670
+ header: { ...files[0].header },
671
+ imports: files.map((f) => {
672
+ return { uri: f.header.id };
673
+ }),
674
+ schemas: {},
675
+ data: [],
676
+ };
677
+ userDefinedOrder.header.id = "USER_DEF";
678
+ let provider = new StackedLayerProvider([
679
+ new InMemoryLayerProvider().AddAll([userDefinedOrder, ...files]),
680
+ new FetchLayerProvider(),
681
+ ]);
682
+ let layerStack = await new IfcxLayerStackBuilder(provider).FromId(userDefinedOrder.header.id).Build();
683
+ if (layerStack instanceof Error) {
684
+ throw layerStack;
685
+ }
686
+ layerStack.GetFederatedLayer().data.forEach((n, i) => {
687
+ n.attributes = n.attributes || {};
688
+ n.attributes[`__internal_${i}`] = n.path;
689
+ });
690
+ return TreeNodeToComposedObject("", layerStack.GetFullTree(), layerStack.GetSchemas());
483
691
  }
484
692
 
485
- // render.ts
693
+ // viewer/render.ts
694
+ // import * as THREE from "three";
695
+ // import { OrbitControls } from "three/addons/controls/OrbitControls.js";
696
+ // import { RGBELoader } from "three/addons/loaders/RGBELoader.js";
697
+ import { PCDLoader } from "three/examples/jsm/loaders/PCDLoader.js";
486
698
  // var controls;
487
699
  // var renderer;
488
700
  var scene;
489
701
  var camera;
490
702
  var datas = [];
491
703
  var autoCamera = true;
492
- // var THREE = window["THREE"];
704
+ var objectMap = {};
705
+ // var domMap = {};
706
+ var primMap = {};
707
+ // var currentPathMapping = null;
708
+ // var rootPrim = null;
709
+ // var selectedObject = null;
710
+ // var selectedDom = null;
711
+ // var raycaster = new THREE.Raycaster();
712
+ // var mouse = new THREE.Vector2();
713
+ var envMap;
493
714
  function init() {
494
715
  scene = new THREE.Scene();
716
+ // const ambient = new THREE.AmbientLight(14544639, 0.4);
717
+ // scene.add(ambient);
718
+ // const keyLight = new THREE.DirectionalLight(16777215, 1);
719
+ // keyLight.position.set(5, -10, 7.5);
720
+ // scene.add(keyLight);
721
+ // const fillLight = new THREE.DirectionalLight(16777215, 0.5);
722
+ // fillLight.position.set(-5, 5, 5);
723
+ // scene.add(fillLight);
724
+ // const rimLight = new THREE.DirectionalLight(16777215, 0.3);
725
+ // rimLight.position.set(0, 8, -10);
726
+ // scene.add(rimLight);
495
727
  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100);
496
728
  camera.up.set(0, 0, 1);
497
729
  camera.position.set(50, 50, 50);
498
730
  camera.lookAt(0, 0, 0);
499
- // const nd = document.querySelector(".viewport");
500
- // renderer = new THREE.WebGLRenderer({
501
- // alpha: true,
502
- // logarithmicDepthBuffer: true,
731
+ scene.add(camera);
732
+ // const nd = document.querySelector(".viewport");
733
+ // renderer = new THREE.WebGLRenderer({
734
+ // alpha: true,
735
+ // logarithmicDepthBuffer: true,
736
+ // });
737
+ // const pmremGenerator = new THREE.PMREMGenerator(renderer);
738
+ // pmremGenerator.compileEquirectangularShader();
739
+ // new RGBELoader().load("images/wildflower_field_1k.hdr", function (texture) {
740
+ // envMap = pmremGenerator.fromEquirectangular(texture).texture;
741
+ // scene.environment = envMap;
742
+ // texture.dispose();
743
+ // pmremGenerator.dispose();
503
744
  // });
504
745
  // renderer.setSize(nd.offsetWidth, nd.offsetHeight);
505
- // controls = new THREE.OrbitControls(camera, renderer.domElement);
746
+ // controls = new OrbitControls(camera, renderer.domElement);
506
747
  // controls.enableDamping = true;
507
748
  // controls.dampingFactor = 0.25;
508
749
  // nd.appendChild(renderer.domElement);
509
- scene.add(camera);
750
+ // renderer.domElement.addEventListener("click", onCanvasClick);
510
751
  return scene;
511
752
  }
512
753
  function HasAttr(node, attrName) {
513
754
  if (!node || !node.attributes) return false;
514
755
  return !!node.attributes[attrName];
515
756
  }
516
- function FindChildWithAttr(node, attrName) {
517
- if (!node || !node.children) return void 0;
518
- for (let i = 0; i < node.children.length; i++) {
519
- if (HasAttr(node.children[i], attrName)) {
520
- return node.children[i];
757
+ // function setHighlight(obj, highlight) {
758
+ // if (!obj) return;
759
+ // obj.traverse((o) => {
760
+ // const mat = o.material;
761
+ // if (mat && mat.color) {
762
+ // if (highlight) {
763
+ // if (!o.userData._origColor) {
764
+ // o.userData._origColor = mat.color.clone();
765
+ // }
766
+ // o.material = mat.clone();
767
+ // o.material.color.set(16711680);
768
+ // } else if (o.userData._origColor) {
769
+ // mat.color.copy(o.userData._origColor);
770
+ // delete o.userData._origColor;
771
+ // }
772
+ // }
773
+ // });
774
+ // }
775
+ // function selectPath(path) {
776
+ // if (!path) {
777
+ // if (selectedObject) setHighlight(selectedObject, false);
778
+ // if (selectedDom) selectedDom.classList.remove("selected");
779
+ // selectedObject = null;
780
+ // selectedDom = null;
781
+ // return;
782
+ // }
783
+ // if (selectedObject) {
784
+ // setHighlight(selectedObject, false);
785
+ // }
786
+ // if (selectedDom) {
787
+ // selectedDom.classList.remove("selected");
788
+ // }
789
+ // selectedObject = objectMap[path] || null;
790
+ // selectedDom = domMap[path] || null;
791
+ // if (selectedObject) setHighlight(selectedObject, true);
792
+ // if (selectedDom) selectedDom.classList.add("selected");
793
+ // }
794
+ // function onCanvasClick(event) {
795
+ // const rect = renderer.domElement.getBoundingClientRect();
796
+ // mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
797
+ // mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
798
+ // raycaster.setFromCamera(mouse, camera);
799
+ // const intersects = raycaster.intersectObjects(Object.values(objectMap), true);
800
+ // if (intersects.length > 0) {
801
+ // let obj = intersects[0].object;
802
+ // while (obj && !obj.userData.path) obj = obj.parent;
803
+ // if (obj && obj.userData.path) {
804
+ // const path = obj.userData.path;
805
+ // const prim = primMap[path];
806
+ // if (prim) {
807
+ // handleClick(prim, currentPathMapping, rootPrim || prim);
808
+ // }
809
+ // selectPath(path);
810
+ // }
811
+ // } else {
812
+ // selectPath(null);
813
+ // }
814
+ // }
815
+ function tryCreateMeshGltfMaterial(path) {
816
+ for (let p of path) {
817
+ if (!p.attributes) {
818
+ continue;
819
+ }
820
+ const pbrMetallicRoughness = p.attributes["gltf::material::pbrMetallicRoughness"];
821
+ const normalTexture = p.attributes["gltf::material::normalTexture"];
822
+ const occlusionTexture = p.attributes["gltf::material::occlusionTexture"];
823
+ const emissiveTexture = p.attributes["gltf::material::emissiveTexture"];
824
+ const emissiveFactor = p.attributes["gltf::material::emissiveFactor"];
825
+ const alphaMode = p.attributes["gltf::material::alphaMode"];
826
+ const alphaCutoff = p.attributes["gltf::material::alphaCutoff"];
827
+ const doubleSided = p.attributes["gltf::material::doubleSided"];
828
+ if (
829
+ !pbrMetallicRoughness &&
830
+ !normalTexture &&
831
+ !occlusionTexture &&
832
+ !emissiveTexture &&
833
+ !emissiveFactor &&
834
+ !alphaMode &&
835
+ !alphaCutoff &&
836
+ !doubleSided
837
+ ) {
838
+ continue;
839
+ }
840
+ let material = new THREE.MeshStandardMaterial();
841
+ material.color = new THREE.Color(1, 1, 1);
842
+ material.metalness = 1;
843
+ material.roughness = 1;
844
+ if (pbrMetallicRoughness) {
845
+ let baseColorFactor = pbrMetallicRoughness["baseColorFactor"];
846
+ if (baseColorFactor) {
847
+ material.color = new THREE.Color(baseColorFactor[0], baseColorFactor[1], baseColorFactor[2]);
848
+ }
849
+ let metallicFactor = pbrMetallicRoughness["metallicFactor"];
850
+ if (metallicFactor !== void 0) {
851
+ material.metalness = metallicFactor;
852
+ }
853
+ let roughnessFactor = pbrMetallicRoughness["roughnessFactor"];
854
+ if (roughnessFactor !== void 0) {
855
+ material.roughness = roughnessFactor;
856
+ }
521
857
  }
858
+ material.envMap = envMap;
859
+ material.needsUpdate = true;
860
+ material.envMapRotation = new THREE.Euler(0.5 * Math.PI, 0, 0);
861
+ return material;
522
862
  }
523
863
  return void 0;
524
864
  }
525
- function createMaterialFromParent(parent, root) {
526
- let reference = parent.attributes["usd::usdshade::materialbindingapi::material::binding"];
865
+ function createMaterialFromParent(path) {
527
866
  let material = {
528
867
  color: new THREE.Color(0.6, 0.6, 0.6),
529
868
  transparent: false,
530
869
  opacity: 1,
531
870
  };
532
- if (reference) {
533
- const materialNode = getChildByName(root, reference.ref);
534
- if (materialNode) {
535
- let color = materialNode?.attributes["bsi::presentation::diffuseColor"];
871
+ for (let p of path) {
872
+ const color = p.attributes ? p.attributes["bsi::ifc::presentation::diffuseColor"] : null;
873
+ if (color) {
536
874
  material.color = new THREE.Color(...color);
537
- if (materialNode?.attributes["bsi::presentation::opacity"]) {
875
+ const opacity = p.attributes["bsi::ifc::presentation::opacity"];
876
+ if (opacity) {
538
877
  material.transparent = true;
539
- material.opacity = materialNode.attributes["bsi::presentation::opacity"];
878
+ material.opacity = opacity;
540
879
  }
880
+ break;
541
881
  }
542
882
  }
543
883
  return material;
544
884
  }
545
- function createCurveFromJson(node, parent, root) {
546
- let points = new Float32Array(node.attributes["usd::usdgeom::basiscurves::points"].flat());
885
+ function createCurveFromJson(path) {
886
+ let points = new Float32Array(path[0].attributes["usd::usdgeom::basiscurves::points"].flat());
547
887
  const geometry = new THREE.BufferGeometry();
548
888
  geometry.setAttribute("position", new THREE.BufferAttribute(points, 3));
549
- const material = createMaterialFromParent(parent, root);
889
+ const material = createMaterialFromParent(path);
550
890
  let lineMaterial = new THREE.LineBasicMaterial({ ...material });
551
891
  lineMaterial.color.multiplyScalar(0.8);
552
892
  return new THREE.Line(geometry, lineMaterial);
553
893
  }
554
- function createMeshFromJson(node, parent, root) {
555
- let points = new Float32Array(node.attributes["usd::usdgeom::mesh::points"].flat());
556
- let indices = new Uint16Array(node.attributes["usd::usdgeom::mesh::faceVertexIndices"]);
894
+ function createMeshFromJson(path) {
895
+ let points = new Float32Array(path[0].attributes["usd::usdgeom::mesh::points"].flat());
896
+ let indices = new Uint16Array(path[0].attributes["usd::usdgeom::mesh::faceVertexIndices"]);
557
897
  const geometry = new THREE.BufferGeometry();
558
898
  geometry.setAttribute("position", new THREE.BufferAttribute(points, 3));
559
899
  geometry.setIndex(new THREE.BufferAttribute(indices, 1));
560
900
  geometry.computeVertexNormals();
561
- const material = createMaterialFromParent(parent, root);
562
- let meshMaterial = new THREE.MeshBasicMaterial({ ...material });
901
+ var meshMaterial;
902
+ let gltfPbrMaterial = tryCreateMeshGltfMaterial(path);
903
+ if (gltfPbrMaterial) {
904
+ meshMaterial = gltfPbrMaterial;
905
+ } else {
906
+ const m = createMaterialFromParent(path);
907
+ meshMaterial = new THREE.MeshLambertMaterial({ ...m });
908
+ }
563
909
  return new THREE.Mesh(geometry, meshMaterial);
564
910
  }
565
- function traverseTree(node, parent, root, parentNode = void 0) {
911
+ function createPointsFromJsonPcdBase64(path) {
912
+ const base64_string = path[0].attributes["pcd::base64"];
913
+ const decoded = atob(base64_string);
914
+ const len = decoded.length;
915
+ const bytes = new Uint8Array(len);
916
+ for (let i = 0; i < len; i++) {
917
+ bytes[i] = decoded.charCodeAt(i);
918
+ }
919
+ const loader = new PCDLoader();
920
+ const points = loader.parse(bytes.buffer);
921
+ points.material.sizeAttenuation = false;
922
+ points.material.size = 2;
923
+ return points;
924
+ }
925
+ function createPoints(geometry, withColors) {
926
+ const material = new THREE.PointsMaterial();
927
+ material.sizeAttenuation = false;
928
+ material.fog = true;
929
+ material.size = 5;
930
+ material.color = new THREE.Color(withColors ? 16777215 : 0);
931
+ if (withColors) {
932
+ material.vertexColors = true;
933
+ }
934
+ return new THREE.Points(geometry, material);
935
+ }
936
+ function createPointsFromJsonArray(path) {
937
+ const geometry = new THREE.BufferGeometry();
938
+ const positions = new Float32Array(path[0].attributes["points::array::positions"].flat());
939
+ geometry.setAttribute("position", new THREE.Float32BufferAttribute(positions, 3));
940
+ const colors = path[0].attributes["points::array::colors"];
941
+ if (colors) {
942
+ const colors_ = new Float32Array(colors.flat());
943
+ geometry.setAttribute("color", new THREE.Float32BufferAttribute(colors_, 3));
944
+ }
945
+ return createPoints(geometry, colors);
946
+ }
947
+ function base64ToArrayBuffer(str) {
948
+ let binary;
949
+ try {
950
+ binary = atob(str);
951
+ } catch (e) {
952
+ throw new Error("base64 encoded string is invalid");
953
+ }
954
+ const bytes = new Uint8Array(binary.length);
955
+ for (let i = 0; i < binary.length; ++i) {
956
+ bytes[i] = binary.charCodeAt(i);
957
+ }
958
+ return bytes.buffer;
959
+ }
960
+ function createPointsFromJsonPositionBase64(path) {
961
+ const geometry = new THREE.BufferGeometry();
962
+ const positions_base64 = path[0].attributes["points::base64::positions"];
963
+ const positions_bytes = base64ToArrayBuffer(positions_base64);
964
+ if (!positions_bytes) {
965
+ return null;
966
+ }
967
+ const positions = new Float32Array(positions_bytes);
968
+ geometry.setAttribute("position", new THREE.Float32BufferAttribute(positions, 3));
969
+ const colors_base64 = path[0].attributes["points::base64::colors"];
970
+ if (colors_base64) {
971
+ const colors_bytes = base64ToArrayBuffer(colors_base64);
972
+ if (colors_bytes) {
973
+ const colors = new Float32Array(colors_bytes);
974
+ geometry.setAttribute("color", new THREE.Float32BufferAttribute(colors, 3));
975
+ }
976
+ }
977
+ return createPoints(geometry, colors_base64);
978
+ }
979
+ function traverseTree(path, parent, pathMapping) {
980
+ const node = path[0];
566
981
  let elem = new THREE.Group();
567
982
  if (HasAttr(node, "usd::usdgeom::visibility::visibility")) {
568
983
  if (node.attributes["usd::usdgeom::visibility::visibility"] === "invisible") {
569
984
  return;
570
985
  }
571
986
  } else if (HasAttr(node, "usd::usdgeom::mesh::points")) {
572
- elem = createMeshFromJson(node, parentNode, root);
987
+ elem = createMeshFromJson(path);
573
988
  } else if (HasAttr(node, "usd::usdgeom::basiscurves::points")) {
574
- elem = createCurveFromJson(node, parentNode, root);
989
+ elem = createCurveFromJson(path);
990
+ } else if (HasAttr(node, "pcd::base64")) {
991
+ elem = createPointsFromJsonPcdBase64(path);
992
+ } else if (HasAttr(node, "points::array::positions")) {
993
+ elem = createPointsFromJsonArray(path);
994
+ } else if (HasAttr(node, "points::base64::positions")) {
995
+ elem = createPointsFromJsonPositionBase64(path);
996
+ }
997
+ objectMap[node.name] = elem;
998
+ primMap[node.name] = node;
999
+ elem.userData.path = node.name;
1000
+ for (let path2 of Object.entries(node.attributes || {})
1001
+ .filter(([k, _]) => k.startsWith("__internal_"))
1002
+ .map(([_, v]) => v)) {
1003
+ (pathMapping[String(path2)] = pathMapping[String(path2)] || []).push(node.name);
575
1004
  }
576
1005
  parent.add(elem);
577
- if (node !== root) {
1006
+ if (path.length > 1) {
578
1007
  elem.matrixAutoUpdate = false;
579
1008
  let matrixNode =
580
1009
  node.attributes && node.attributes["usd::xformop::transform"]
@@ -587,7 +1016,7 @@ function traverseTree(node, parent, root, parentNode = void 0) {
587
1016
  elem.matrix = matrix;
588
1017
  }
589
1018
  }
590
- (node.children || []).forEach((child) => traverseTree(child, elem || parent, root, node));
1019
+ (node.children || []).forEach((child) => traverseTree([child, ...path], elem || parent, pathMapping));
591
1020
  }
592
1021
  // function encodeHtmlEntities(str) {
593
1022
  // const div = document.createElement("div");
@@ -598,44 +1027,120 @@ function traverseTree(node, parent, root, parentNode = void 0) {
598
1027
  // "usd::usdgeom::mesh::points": "deployed_code",
599
1028
  // "usd::usdgeom::basiscurves::points": "line_curve",
600
1029
  // "usd::usdshade::material::outputs::surface.connect": "line_style",
1030
+ // "pcd::base64": "grain",
1031
+ // "points::array::positions": "grain",
1032
+ // "points::base64::positions": "grain",
601
1033
  // };
602
- // function buildDomTree(prim, node) {
1034
+ // function handleClick(prim, pathMapping, root) {
1035
+ // const container = document.querySelector(".attributes .table");
1036
+ // if (container !== null) {
1037
+ // container.innerHTML = "";
1038
+ // const table = document.createElement("table");
1039
+ // table.setAttribute("border", "0");
1040
+ // const entries = [
1041
+ // ["name", prim.name],
1042
+ // ...Object.entries(prim.attributes).filter(([k, _]) => !k.startsWith("__internal_")),
1043
+ // ];
1044
+ // const format = (value) => {
1045
+ // if (Array.isArray(value)) {
1046
+ // let N = document.createElement("span");
1047
+ // N.appendChild(document.createTextNode("("));
1048
+ // let first = true;
1049
+ // for (let n of value.map(format)) {
1050
+ // if (!first) {
1051
+ // N.appendChild(document.createTextNode(","));
1052
+ // }
1053
+ // N.appendChild(n);
1054
+ // first = false;
1055
+ // }
1056
+ // N.appendChild(document.createTextNode(")"));
1057
+ // return N;
1058
+ // } else if (typeof value === "object") {
1059
+ // const ks = Object.keys(value);
1060
+ // if (ks.length == 1 && ks[0] === "ref" && pathMapping[value.ref] && pathMapping[value.ref].length == 1) {
1061
+ // let a = document.createElement("a");
1062
+ // let resolvedRefAsPath = pathMapping[value.ref][0];
1063
+ // a.setAttribute("href", "#");
1064
+ // a.textContent = resolvedRefAsPath;
1065
+ // a.onclick = () => {
1066
+ // let prim2 = null;
1067
+ // const recurse = (n) => {
1068
+ // if (n.name === resolvedRefAsPath) {
1069
+ // prim2 = n;
1070
+ // } else {
1071
+ // (n.children || []).forEach(recurse);
1072
+ // }
1073
+ // };
1074
+ // recurse(root);
1075
+ // if (prim2) {
1076
+ // handleClick(prim2, pathMapping, root);
1077
+ // }
1078
+ // };
1079
+ // return a;
1080
+ // } else {
1081
+ // return document.createTextNode(JSON.stringify(value));
1082
+ // }
1083
+ // } else {
1084
+ // return document.createTextNode(value);
1085
+ // }
1086
+ // };
1087
+ // entries.forEach(([key, value]) => {
1088
+ // const tr = document.createElement("tr");
1089
+ // const tdKey = document.createElement("td");
1090
+ // tdKey.textContent = encodeHtmlEntities(key);
1091
+ // const tdValue = document.createElement("td");
1092
+ // tdValue.appendChild(format(value));
1093
+ // tr.appendChild(tdKey);
1094
+ // tr.appendChild(tdValue);
1095
+ // table.appendChild(tr);
1096
+ // });
1097
+ // container.appendChild(table);
1098
+ // }
1099
+ // }
1100
+ // function buildDomTree(prim, node, pathMapping, root = null) {
603
1101
  // const elem = document.createElement("div");
604
1102
  // let span;
605
1103
  // elem.appendChild(document.createTextNode(prim.name ? prim.name.split("/").reverse()[0] : "root"));
606
1104
  // elem.appendChild((span = document.createElement("span")));
607
1105
  // Object.entries(icons).forEach(([k, v]) => (span.innerText += (prim.attributes || {})[k] ? v : " "));
608
1106
  // span.className = "material-symbols-outlined";
1107
+ // domMap[prim.name] = elem;
1108
+ // elem.dataset.path = prim.name;
609
1109
  // elem.onclick = (evt) => {
610
- // let rows = [["name", prim.name]]
611
- // .concat(Object.entries(prim.attributes))
612
- // .map(
613
- // ([k, v]) =>
614
- // `<tr><td>${encodeHtmlEntities(k)}</td><td>${encodeHtmlEntities(typeof v === "object" ? JSON.stringify(v) : v)}</td>`
615
- // )
616
- // .join("");
617
- // document.querySelector(".attributes .table").innerHTML = `<table border="0">${rows}</table>`;
1110
+ // handleClick(prim, pathMapping, root || prim);
1111
+ // selectPath(prim.name);
618
1112
  // evt.stopPropagation();
619
1113
  // };
620
1114
  // node.appendChild(elem);
621
- // (prim.children || []).forEach((p) => buildDomTree(p, elem));
1115
+ // (prim.children || []).forEach((p) => buildDomTree(p, elem, pathMapping, root || prim));
622
1116
  // }
623
- function composeAndRender() {
1117
+ async function composeAndRender() {
624
1118
  if (scene) {
625
1119
  scene.children = [];
626
1120
  }
1121
+ objectMap = {};
1122
+ // domMap = {};
1123
+ primMap = {};
1124
+ // currentPathMapping = null;
1125
+ // rootPrim = null;
627
1126
  // document.querySelector(".tree").innerHTML = "";
628
1127
  if (datas.length === 0) {
629
1128
  return;
630
1129
  }
631
1130
  let tree = null;
632
1131
  let dataArray = datas.map((arr) => arr[1]);
633
- tree = compose3(dataArray);
1132
+ tree = await compose3(dataArray);
634
1133
  if (!tree) {
635
1134
  console.error("No result from composition");
636
1135
  return;
637
1136
  }
638
- traverseTree(tree, scene || init(), tree);
1137
+ if (!scene) {
1138
+ init();
1139
+ }
1140
+ let pathMapping = {};
1141
+ traverseTree([tree], scene, pathMapping);
1142
+ // currentPathMapping = pathMapping;
1143
+ // rootPrim = tree;
639
1144
  if (autoCamera) {
640
1145
  const boundingBox = new THREE.Box3();
641
1146
  boundingBox.setFromObject(scene);
@@ -650,7 +1155,7 @@ function composeAndRender() {
650
1155
  autoCamera = false;
651
1156
  }
652
1157
  }
653
- // buildDomTree(tree, document.querySelector(".tree"));
1158
+ // buildDomTree(tree, document.querySelector(".tree"), pathMapping);
654
1159
  // animate();
655
1160
  }
656
1161
  // function createLayerDom() {
@@ -682,10 +1187,10 @@ function composeAndRender() {
682
1187
  // document.querySelector(".layers div").appendChild(elem);
683
1188
  // });
684
1189
  // }
685
- // function addModel(name, m) {
1190
+ // async function addModel(name, m) {
686
1191
  // datas.push([name, m]);
687
1192
  // createLayerDom();
688
- // composeAndRender();
1193
+ // await composeAndRender();
689
1194
  // }
690
1195
  // function animate() {
691
1196
  // requestAnimationFrame(animate);
@@ -694,9 +1199,9 @@ function composeAndRender() {
694
1199
  // }
695
1200
  // export { composeAndRender, addModel as default };
696
1201
 
697
- export function parse(m, name) {
1202
+ export async function parse(m, name) {
698
1203
  datas.push([name, m]);
699
- composeAndRender();
1204
+ await composeAndRender();
700
1205
  return scene;
701
1206
  }
702
1207
  export function clear() {