@2112-lab/central-plant 0.3.16 → 0.3.18

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.
@@ -18160,19 +18160,700 @@ function addPrimitiveAttributes( geometry, primitiveDef, parser ) {
18160
18160
 
18161
18161
  }
18162
18162
 
18163
+ const _taskCache = new WeakMap();
18164
+
18165
+ /**
18166
+ * A loader for the Draco format.
18167
+ *
18168
+ * [Draco]{@link https://google.github.io/draco/} is an open source library for compressing
18169
+ * and decompressing 3D meshes and point clouds. Compressed geometry can be significantly smaller,
18170
+ * at the cost of additional decoding time on the client device.
18171
+ *
18172
+ * Standalone Draco files have a `.drc` extension, and contain vertex positions, normals, colors,
18173
+ * and other attributes. Draco files do not contain materials, textures, animation, or node hierarchies –
18174
+ * to use these features, embed Draco geometry inside of a glTF file. A normal glTF file can be converted
18175
+ * to a Draco-compressed glTF file using [glTF-Pipeline]{@link https://github.com/CesiumGS/gltf-pipeline}.
18176
+ * When using Draco with glTF, an instance of `DRACOLoader` will be used internally by {@link GLTFLoader}.
18177
+ *
18178
+ * It is recommended to create one DRACOLoader instance and reuse it to avoid loading and creating
18179
+ * multiple decoder instances.
18180
+ *
18181
+ * `DRACOLoader` will automatically use either the JS or the WASM decoding library, based on
18182
+ * browser capabilities.
18183
+ *
18184
+ * ```js
18185
+ * const loader = new DRACOLoader();
18186
+ * loader.setDecoderPath( '/examples/jsm/libs/draco/' );
18187
+ *
18188
+ * const geometry = await dracoLoader.loadAsync( 'models/draco/bunny.drc' );
18189
+ * geometry.computeVertexNormals(); // optional
18190
+ *
18191
+ * dracoLoader.dispose();
18192
+ * ```
18193
+ *
18194
+ * @augments Loader
18195
+ * @three_import import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
18196
+ */
18197
+ class DRACOLoader extends THREE.Loader {
18198
+
18199
+ /**
18200
+ * Constructs a new Draco loader.
18201
+ *
18202
+ * @param {LoadingManager} [manager] - The loading manager.
18203
+ */
18204
+ constructor( manager ) {
18205
+
18206
+ super( manager );
18207
+
18208
+ this.decoderPath = '';
18209
+ this.decoderConfig = {};
18210
+ this.decoderBinary = null;
18211
+ this.decoderPending = null;
18212
+
18213
+ this.workerLimit = 4;
18214
+ this.workerPool = [];
18215
+ this.workerNextTaskID = 1;
18216
+ this.workerSourceURL = '';
18217
+
18218
+ this.defaultAttributeIDs = {
18219
+ position: 'POSITION',
18220
+ normal: 'NORMAL',
18221
+ color: 'COLOR',
18222
+ uv: 'TEX_COORD'
18223
+ };
18224
+ this.defaultAttributeTypes = {
18225
+ position: 'Float32Array',
18226
+ normal: 'Float32Array',
18227
+ color: 'Float32Array',
18228
+ uv: 'Float32Array'
18229
+ };
18230
+
18231
+ }
18232
+
18233
+ /**
18234
+ * Provides configuration for the decoder libraries. Configuration cannot be changed after decoding begins.
18235
+ *
18236
+ * @param {string} path - The decoder path.
18237
+ * @return {DRACOLoader} A reference to this loader.
18238
+ */
18239
+ setDecoderPath( path ) {
18240
+
18241
+ this.decoderPath = path;
18242
+
18243
+ return this;
18244
+
18245
+ }
18246
+
18247
+ /**
18248
+ * Provides configuration for the decoder libraries. Configuration cannot be changed after decoding begins.
18249
+ *
18250
+ * @param {{type:('js'|'wasm')}} config - The decoder config.
18251
+ * @return {DRACOLoader} A reference to this loader.
18252
+ */
18253
+ setDecoderConfig( config ) {
18254
+
18255
+ this.decoderConfig = config;
18256
+
18257
+ return this;
18258
+
18259
+ }
18260
+
18261
+ /**
18262
+ * Sets the maximum number of Web Workers to be used during decoding.
18263
+ * A lower limit may be preferable if workers are also for other tasks in the application.
18264
+ *
18265
+ * @param {number} workerLimit - The worker limit.
18266
+ * @return {DRACOLoader} A reference to this loader.
18267
+ */
18268
+ setWorkerLimit( workerLimit ) {
18269
+
18270
+ this.workerLimit = workerLimit;
18271
+
18272
+ return this;
18273
+
18274
+ }
18275
+
18276
+ /**
18277
+ * Starts loading from the given URL and passes the loaded Draco asset
18278
+ * to the `onLoad()` callback.
18279
+ *
18280
+ * @param {string} url - The path/URL of the file to be loaded. This can also be a data URI.
18281
+ * @param {function(BufferGeometry)} onLoad - Executed when the loading process has been finished.
18282
+ * @param {onProgressCallback} onProgress - Executed while the loading is in progress.
18283
+ * @param {onErrorCallback} onError - Executed when errors occur.
18284
+ */
18285
+ load( url, onLoad, onProgress, onError ) {
18286
+
18287
+ const loader = new THREE.FileLoader( this.manager );
18288
+
18289
+ loader.setPath( this.path );
18290
+ loader.setResponseType( 'arraybuffer' );
18291
+ loader.setRequestHeader( this.requestHeader );
18292
+ loader.setWithCredentials( this.withCredentials );
18293
+
18294
+ loader.load( url, ( buffer ) => {
18295
+
18296
+ this.parse( buffer, onLoad, onError );
18297
+
18298
+ }, onProgress, onError );
18299
+
18300
+ }
18301
+
18302
+ /**
18303
+ * Parses the given Draco data.
18304
+ *
18305
+ * @param {ArrayBuffer} buffer - The raw Draco data as an array buffer.
18306
+ * @param {function(BufferGeometry)} onLoad - Executed when the loading/parsing process has been finished.
18307
+ * @param {onErrorCallback} onError - Executed when errors occur.
18308
+ */
18309
+ parse( buffer, onLoad, onError = ()=>{} ) {
18310
+
18311
+ this.decodeDracoFile( buffer, onLoad, null, null, THREE.SRGBColorSpace, onError ).catch( onError );
18312
+
18313
+ }
18314
+
18315
+ //
18316
+
18317
+ decodeDracoFile( buffer, callback, attributeIDs, attributeTypes, vertexColorSpace = THREE.LinearSRGBColorSpace, onError = () => {} ) {
18318
+
18319
+ const taskConfig = {
18320
+ attributeIDs: attributeIDs || this.defaultAttributeIDs,
18321
+ attributeTypes: attributeTypes || this.defaultAttributeTypes,
18322
+ useUniqueIDs: !! attributeIDs,
18323
+ vertexColorSpace: vertexColorSpace,
18324
+ };
18325
+
18326
+ return this.decodeGeometry( buffer, taskConfig ).then( callback ).catch( onError );
18327
+
18328
+ }
18329
+
18330
+ decodeGeometry( buffer, taskConfig ) {
18331
+
18332
+ const taskKey = JSON.stringify( taskConfig );
18333
+
18334
+ // Check for an existing task using this buffer. A transferred buffer cannot be transferred
18335
+ // again from this thread.
18336
+ if ( _taskCache.has( buffer ) ) {
18337
+
18338
+ const cachedTask = _taskCache.get( buffer );
18339
+
18340
+ if ( cachedTask.key === taskKey ) {
18341
+
18342
+ return cachedTask.promise;
18343
+
18344
+ } else if ( buffer.byteLength === 0 ) {
18345
+
18346
+ // Technically, it would be possible to wait for the previous task to complete,
18347
+ // transfer the buffer back, and decode again with the second configuration. That
18348
+ // is complex, and I don't know of any reason to decode a Draco buffer twice in
18349
+ // different ways, so this is left unimplemented.
18350
+ throw new Error(
18351
+
18352
+ 'THREE.DRACOLoader: Unable to re-decode a buffer with different ' +
18353
+ 'settings. Buffer has already been transferred.'
18354
+
18355
+ );
18356
+
18357
+ }
18358
+
18359
+ }
18360
+
18361
+ //
18362
+
18363
+ let worker;
18364
+ const taskID = this.workerNextTaskID ++;
18365
+ const taskCost = buffer.byteLength;
18366
+
18367
+ // Obtain a worker and assign a task, and construct a geometry instance
18368
+ // when the task completes.
18369
+ const geometryPending = this._getWorker( taskID, taskCost )
18370
+ .then( ( _worker ) => {
18371
+
18372
+ worker = _worker;
18373
+
18374
+ return new Promise( ( resolve, reject ) => {
18375
+
18376
+ worker._callbacks[ taskID ] = { resolve, reject };
18377
+
18378
+ worker.postMessage( { type: 'decode', id: taskID, taskConfig, buffer }, [ buffer ] );
18379
+
18380
+ // this.debug();
18381
+
18382
+ } );
18383
+
18384
+ } )
18385
+ .then( ( message ) => this._createGeometry( message.geometry ) );
18386
+
18387
+ // Remove task from the task list.
18388
+ // Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416)
18389
+ geometryPending
18390
+ .catch( () => true )
18391
+ .then( () => {
18392
+
18393
+ if ( worker && taskID ) {
18394
+
18395
+ this._releaseTask( worker, taskID );
18396
+
18397
+ // this.debug();
18398
+
18399
+ }
18400
+
18401
+ } );
18402
+
18403
+ // Cache the task result.
18404
+ _taskCache.set( buffer, {
18405
+
18406
+ key: taskKey,
18407
+ promise: geometryPending
18408
+
18409
+ } );
18410
+
18411
+ return geometryPending;
18412
+
18413
+ }
18414
+
18415
+ _createGeometry( geometryData ) {
18416
+
18417
+ const geometry = new THREE.BufferGeometry();
18418
+
18419
+ if ( geometryData.index ) {
18420
+
18421
+ geometry.setIndex( new THREE.BufferAttribute( geometryData.index.array, 1 ) );
18422
+
18423
+ }
18424
+
18425
+ for ( let i = 0; i < geometryData.attributes.length; i ++ ) {
18426
+
18427
+ const result = geometryData.attributes[ i ];
18428
+ const name = result.name;
18429
+ const array = result.array;
18430
+ const itemSize = result.itemSize;
18431
+
18432
+ const attribute = new THREE.BufferAttribute( array, itemSize );
18433
+
18434
+ if ( name === 'color' ) {
18435
+
18436
+ this._assignVertexColorSpace( attribute, result.vertexColorSpace );
18437
+
18438
+ attribute.normalized = ( array instanceof Float32Array ) === false;
18439
+
18440
+ }
18441
+
18442
+ geometry.setAttribute( name, attribute );
18443
+
18444
+ }
18445
+
18446
+ return geometry;
18447
+
18448
+ }
18449
+
18450
+ _assignVertexColorSpace( attribute, inputColorSpace ) {
18451
+
18452
+ // While .drc files do not specify colorspace, the only 'official' tooling
18453
+ // is PLY and OBJ converters, which use sRGB. We'll assume sRGB when a .drc
18454
+ // file is passed into .load() or .parse(). GLTFLoader uses internal APIs
18455
+ // to decode geometry, and vertex colors are already Linear-sRGB in there.
18456
+
18457
+ if ( inputColorSpace !== THREE.SRGBColorSpace ) return;
18458
+
18459
+ const _color = new THREE.Color();
18460
+
18461
+ for ( let i = 0, il = attribute.count; i < il; i ++ ) {
18462
+
18463
+ _color.fromBufferAttribute( attribute, i );
18464
+ THREE.ColorManagement.colorSpaceToWorking( _color, THREE.SRGBColorSpace );
18465
+ attribute.setXYZ( i, _color.r, _color.g, _color.b );
18466
+
18467
+ }
18468
+
18469
+ }
18470
+
18471
+ _loadLibrary( url, responseType ) {
18472
+
18473
+ const loader = new THREE.FileLoader( this.manager );
18474
+ loader.setPath( this.decoderPath );
18475
+ loader.setResponseType( responseType );
18476
+ loader.setWithCredentials( this.withCredentials );
18477
+
18478
+ return new Promise( ( resolve, reject ) => {
18479
+
18480
+ loader.load( url, resolve, undefined, reject );
18481
+
18482
+ } );
18483
+
18484
+ }
18485
+
18486
+ preload() {
18487
+
18488
+ this._initDecoder();
18489
+
18490
+ return this;
18491
+
18492
+ }
18493
+
18494
+ _initDecoder() {
18495
+
18496
+ if ( this.decoderPending ) return this.decoderPending;
18497
+
18498
+ const useJS = typeof WebAssembly !== 'object' || this.decoderConfig.type === 'js';
18499
+ const librariesPending = [];
18500
+
18501
+ if ( useJS ) {
18502
+
18503
+ librariesPending.push( this._loadLibrary( 'draco_decoder.js', 'text' ) );
18504
+
18505
+ } else {
18506
+
18507
+ librariesPending.push( this._loadLibrary( 'draco_wasm_wrapper.js', 'text' ) );
18508
+ librariesPending.push( this._loadLibrary( 'draco_decoder.wasm', 'arraybuffer' ) );
18509
+
18510
+ }
18511
+
18512
+ this.decoderPending = Promise.all( librariesPending )
18513
+ .then( ( libraries ) => {
18514
+
18515
+ const jsContent = libraries[ 0 ];
18516
+
18517
+ if ( ! useJS ) {
18518
+
18519
+ this.decoderConfig.wasmBinary = libraries[ 1 ];
18520
+
18521
+ }
18522
+
18523
+ const fn = DRACOWorker.toString();
18524
+
18525
+ const body = [
18526
+ '/* draco decoder */',
18527
+ jsContent,
18528
+ '',
18529
+ '/* worker */',
18530
+ fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) )
18531
+ ].join( '\n' );
18532
+
18533
+ this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) );
18534
+
18535
+ } );
18536
+
18537
+ return this.decoderPending;
18538
+
18539
+ }
18540
+
18541
+ _getWorker( taskID, taskCost ) {
18542
+
18543
+ return this._initDecoder().then( () => {
18544
+
18545
+ if ( this.workerPool.length < this.workerLimit ) {
18546
+
18547
+ const worker = new Worker( this.workerSourceURL );
18548
+
18549
+ worker._callbacks = {};
18550
+ worker._taskCosts = {};
18551
+ worker._taskLoad = 0;
18552
+
18553
+ worker.postMessage( { type: 'init', decoderConfig: this.decoderConfig } );
18554
+
18555
+ worker.onmessage = function ( e ) {
18556
+
18557
+ const message = e.data;
18558
+
18559
+ switch ( message.type ) {
18560
+
18561
+ case 'decode':
18562
+ worker._callbacks[ message.id ].resolve( message );
18563
+ break;
18564
+
18565
+ case 'error':
18566
+ worker._callbacks[ message.id ].reject( message );
18567
+ break;
18568
+
18569
+ default:
18570
+ console.error( 'THREE.DRACOLoader: Unexpected message, "' + message.type + '"' );
18571
+
18572
+ }
18573
+
18574
+ };
18575
+
18576
+ this.workerPool.push( worker );
18577
+
18578
+ } else {
18579
+
18580
+ this.workerPool.sort( function ( a, b ) {
18581
+
18582
+ return a._taskLoad > b._taskLoad ? - 1 : 1;
18583
+
18584
+ } );
18585
+
18586
+ }
18587
+
18588
+ const worker = this.workerPool[ this.workerPool.length - 1 ];
18589
+ worker._taskCosts[ taskID ] = taskCost;
18590
+ worker._taskLoad += taskCost;
18591
+ return worker;
18592
+
18593
+ } );
18594
+
18595
+ }
18596
+
18597
+ _releaseTask( worker, taskID ) {
18598
+
18599
+ worker._taskLoad -= worker._taskCosts[ taskID ];
18600
+ delete worker._callbacks[ taskID ];
18601
+ delete worker._taskCosts[ taskID ];
18602
+
18603
+ }
18604
+
18605
+ debug() {
18606
+
18607
+ console.log( 'Task load: ', this.workerPool.map( ( worker ) => worker._taskLoad ) );
18608
+
18609
+ }
18610
+
18611
+ dispose() {
18612
+
18613
+ for ( let i = 0; i < this.workerPool.length; ++ i ) {
18614
+
18615
+ this.workerPool[ i ].terminate();
18616
+
18617
+ }
18618
+
18619
+ this.workerPool.length = 0;
18620
+
18621
+ if ( this.workerSourceURL !== '' ) {
18622
+
18623
+ URL.revokeObjectURL( this.workerSourceURL );
18624
+
18625
+ }
18626
+
18627
+ return this;
18628
+
18629
+ }
18630
+
18631
+ }
18632
+
18633
+ /* WEB WORKER */
18634
+
18635
+ function DRACOWorker() {
18636
+
18637
+ let decoderConfig;
18638
+ let decoderPending;
18639
+
18640
+ onmessage = function ( e ) {
18641
+
18642
+ const message = e.data;
18643
+
18644
+ switch ( message.type ) {
18645
+
18646
+ case 'init':
18647
+ decoderConfig = message.decoderConfig;
18648
+ decoderPending = new Promise( function ( resolve/*, reject*/ ) {
18649
+
18650
+ decoderConfig.onModuleLoaded = function ( draco ) {
18651
+
18652
+ // Module is Promise-like. Wrap before resolving to avoid loop.
18653
+ resolve( { draco: draco } );
18654
+
18655
+ };
18656
+
18657
+ DracoDecoderModule( decoderConfig ); // eslint-disable-line no-undef
18658
+
18659
+ } );
18660
+ break;
18661
+
18662
+ case 'decode':
18663
+ const buffer = message.buffer;
18664
+ const taskConfig = message.taskConfig;
18665
+ decoderPending.then( ( module ) => {
18666
+
18667
+ const draco = module.draco;
18668
+ const decoder = new draco.Decoder();
18669
+
18670
+ try {
18671
+
18672
+ const geometry = decodeGeometry( draco, decoder, new Int8Array( buffer ), taskConfig );
18673
+
18674
+ const buffers = geometry.attributes.map( ( attr ) => attr.array.buffer );
18675
+
18676
+ if ( geometry.index ) buffers.push( geometry.index.array.buffer );
18677
+
18678
+ self.postMessage( { type: 'decode', id: message.id, geometry }, buffers );
18679
+
18680
+ } catch ( error ) {
18681
+
18682
+ console.error( error );
18683
+
18684
+ self.postMessage( { type: 'error', id: message.id, error: error.message } );
18685
+
18686
+ } finally {
18687
+
18688
+ draco.destroy( decoder );
18689
+
18690
+ }
18691
+
18692
+ } );
18693
+ break;
18694
+
18695
+ }
18696
+
18697
+ };
18698
+
18699
+ function decodeGeometry( draco, decoder, array, taskConfig ) {
18700
+
18701
+ const attributeIDs = taskConfig.attributeIDs;
18702
+ const attributeTypes = taskConfig.attributeTypes;
18703
+
18704
+ let dracoGeometry;
18705
+ let decodingStatus;
18706
+
18707
+ const geometryType = decoder.GetEncodedGeometryType( array );
18708
+
18709
+ if ( geometryType === draco.TRIANGULAR_MESH ) {
18710
+
18711
+ dracoGeometry = new draco.Mesh();
18712
+ decodingStatus = decoder.DecodeArrayToMesh( array, array.byteLength, dracoGeometry );
18713
+
18714
+ } else if ( geometryType === draco.POINT_CLOUD ) {
18715
+
18716
+ dracoGeometry = new draco.PointCloud();
18717
+ decodingStatus = decoder.DecodeArrayToPointCloud( array, array.byteLength, dracoGeometry );
18718
+
18719
+ } else {
18720
+
18721
+ throw new Error( 'THREE.DRACOLoader: Unexpected geometry type.' );
18722
+
18723
+ }
18724
+
18725
+ if ( ! decodingStatus.ok() || dracoGeometry.ptr === 0 ) {
18726
+
18727
+ throw new Error( 'THREE.DRACOLoader: Decoding failed: ' + decodingStatus.error_msg() );
18728
+
18729
+ }
18730
+
18731
+ const geometry = { index: null, attributes: [] };
18732
+
18733
+ // Gather all vertex attributes.
18734
+ for ( const attributeName in attributeIDs ) {
18735
+
18736
+ const attributeType = self[ attributeTypes[ attributeName ] ];
18737
+
18738
+ let attribute;
18739
+ let attributeID;
18740
+
18741
+ // A Draco file may be created with default vertex attributes, whose attribute IDs
18742
+ // are mapped 1:1 from their semantic name (POSITION, NORMAL, ...). Alternatively,
18743
+ // a Draco file may contain a custom set of attributes, identified by known unique
18744
+ // IDs. glTF files always do the latter, and `.drc` files typically do the former.
18745
+ if ( taskConfig.useUniqueIDs ) {
18746
+
18747
+ attributeID = attributeIDs[ attributeName ];
18748
+ attribute = decoder.GetAttributeByUniqueId( dracoGeometry, attributeID );
18749
+
18750
+ } else {
18751
+
18752
+ attributeID = decoder.GetAttributeId( dracoGeometry, draco[ attributeIDs[ attributeName ] ] );
18753
+
18754
+ if ( attributeID === - 1 ) continue;
18755
+
18756
+ attribute = decoder.GetAttribute( dracoGeometry, attributeID );
18757
+
18758
+ }
18759
+
18760
+ const attributeResult = decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute );
18761
+
18762
+ if ( attributeName === 'color' ) {
18763
+
18764
+ attributeResult.vertexColorSpace = taskConfig.vertexColorSpace;
18765
+
18766
+ }
18767
+
18768
+ geometry.attributes.push( attributeResult );
18769
+
18770
+ }
18771
+
18772
+ // Add index.
18773
+ if ( geometryType === draco.TRIANGULAR_MESH ) {
18774
+
18775
+ geometry.index = decodeIndex( draco, decoder, dracoGeometry );
18776
+
18777
+ }
18778
+
18779
+ draco.destroy( dracoGeometry );
18780
+
18781
+ return geometry;
18782
+
18783
+ }
18784
+
18785
+ function decodeIndex( draco, decoder, dracoGeometry ) {
18786
+
18787
+ const numFaces = dracoGeometry.num_faces();
18788
+ const numIndices = numFaces * 3;
18789
+ const byteLength = numIndices * 4;
18790
+
18791
+ const ptr = draco._malloc( byteLength );
18792
+ decoder.GetTrianglesUInt32Array( dracoGeometry, byteLength, ptr );
18793
+ const index = new Uint32Array( draco.HEAPF32.buffer, ptr, numIndices ).slice();
18794
+ draco._free( ptr );
18795
+
18796
+ return { array: index, itemSize: 1 };
18797
+
18798
+ }
18799
+
18800
+ function decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) {
18801
+
18802
+ const numComponents = attribute.num_components();
18803
+ const numPoints = dracoGeometry.num_points();
18804
+ const numValues = numPoints * numComponents;
18805
+ const byteLength = numValues * attributeType.BYTES_PER_ELEMENT;
18806
+ const dataType = getDracoDataType( draco, attributeType );
18807
+
18808
+ const ptr = draco._malloc( byteLength );
18809
+ decoder.GetAttributeDataArrayForAllPoints( dracoGeometry, attribute, dataType, byteLength, ptr );
18810
+ const array = new attributeType( draco.HEAPF32.buffer, ptr, numValues ).slice();
18811
+ draco._free( ptr );
18812
+
18813
+ return {
18814
+ name: attributeName,
18815
+ array: array,
18816
+ itemSize: numComponents
18817
+ };
18818
+
18819
+ }
18820
+
18821
+ function getDracoDataType( draco, attributeType ) {
18822
+
18823
+ switch ( attributeType ) {
18824
+
18825
+ case Float32Array: return draco.DT_FLOAT32;
18826
+ case Int8Array: return draco.DT_INT8;
18827
+ case Int16Array: return draco.DT_INT16;
18828
+ case Int32Array: return draco.DT_INT32;
18829
+ case Uint8Array: return draco.DT_UINT8;
18830
+ case Uint16Array: return draco.DT_UINT16;
18831
+ case Uint32Array: return draco.DT_UINT32;
18832
+
18833
+ }
18834
+
18835
+ }
18836
+
18837
+ }
18838
+
18163
18839
  var ModelPreloader = /*#__PURE__*/function () {
18164
18840
  function ModelPreloader() {
18165
18841
  _classCallCheck(this, ModelPreloader);
18166
18842
  this.modelCache = new Map(); // Cache for loaded models
18167
18843
  this.loadingPromises = new Map(); // Track ongoing loads to prevent duplicates
18168
18844
  this.gltfLoader = new GLTFLoader();
18845
+
18846
+ // Setup DRACO decoder for compressed GLB files
18847
+ this.dracoLoader = new DRACOLoader();
18848
+ this.dracoLoader.setDecoderPath('/draco/');
18849
+ this.gltfLoader.setDRACOLoader(this.dracoLoader);
18169
18850
  this.isPreloading = false;
18170
18851
  this.preloadingPromise = null;
18171
18852
  this.componentDictionary = null;
18172
18853
  this.modelsBasePath = '/library/models/'; // Default local path
18173
18854
  this.urlResolver = null; // Optional function to resolve model URLs (for S3 authentication)
18174
18855
 
18175
- console.log('🚀 ModelPreloader initialized with GLB support');
18856
+ console.log('🚀 ModelPreloader initialized with GLB + DRACO support');
18176
18857
  }
18177
18858
 
18178
18859
  /**
@@ -19873,6 +20554,114 @@ var ComponentDataManager = /*#__PURE__*/function (_BaseDisposable) {
19873
20554
  }]);
19874
20555
  }(BaseDisposable);
19875
20556
 
20557
+ /**
20558
+ * Utility function to update userData.direction for components after 90-degree rotation
20559
+ * This function handles the direction vector transformation for connectors when their parent component is rotated
20560
+ *
20561
+ * @param {THREE.Object3D} component - The component that was rotated
20562
+ * @param {string} axis - The axis of rotation ('x', 'y', or 'z')
20563
+ * @param {number} degrees - The rotation angle in degrees (should be multiple of 90)
20564
+ */
20565
+ function updateDirectionAfterRotation(component, axis, degrees) {
20566
+ if (!component) {
20567
+ console.warn('⚠️ updateDirectionAfterRotation: No component provided');
20568
+ return;
20569
+ }
20570
+
20571
+ // Only handle 90-degree increments
20572
+ if (degrees % 90 !== 0) {
20573
+ console.warn('⚠️ updateDirectionAfterRotation: Only 90-degree increments are supported');
20574
+ return;
20575
+ }
20576
+
20577
+ // Normalize degrees to 0-360 range
20578
+ var normalizedDegrees = (degrees % 360 + 360) % 360;
20579
+ console.log("\uD83D\uDD04 Updating direction vectors for ".concat(component.name || component.uuid, " after ").concat(degrees, "\xB0 rotation around ").concat(axis, " axis"));
20580
+
20581
+ // Traverse all children (connectors) and update their direction vectors
20582
+ component.traverse(function (child) {
20583
+ var _child$userData, _child$userData2;
20584
+ var childType = ((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) || ((_child$userData2 = child.userData) === null || _child$userData2 === void 0 ? void 0 : _child$userData2.objectType);
20585
+ if (child.userData && Array.isArray(child.userData.direction) && childType === 'connector') {
20586
+ var originalDirection = _toConsumableArray(child.userData.direction);
20587
+ var newDirection = rotateDirectionVector(originalDirection, axis, normalizedDegrees);
20588
+
20589
+ // Update the direction
20590
+ child.userData.direction = newDirection;
20591
+ console.log("\uD83D\uDCCD Updated connector ".concat(child.name || child.uuid, " direction:"), {
20592
+ original: originalDirection,
20593
+ new: newDirection,
20594
+ rotationAxis: axis,
20595
+ rotationDegrees: degrees
20596
+ });
20597
+ }
20598
+ });
20599
+ }
20600
+
20601
+ /**
20602
+ * Rotate a direction vector by 90-degree increments around a specific axis
20603
+ *
20604
+ * @param {Array<number>} direction - The original direction vector [x, y, z]
20605
+ * @param {string} axis - The axis of rotation ('x', 'y', or 'z')
20606
+ * @param {number} degrees - The rotation angle in degrees (must be multiple of 90)
20607
+ * @returns {Array<number>} The new direction vector [x, y, z]
20608
+ */
20609
+ function rotateDirectionVector(direction, axis, degrees) {
20610
+ if (!Array.isArray(direction) || direction.length !== 3) {
20611
+ console.warn('⚠️ rotateDirectionVector: Invalid direction vector');
20612
+ return direction;
20613
+ }
20614
+ var _direction = _slicedToArray(direction, 3),
20615
+ x = _direction[0],
20616
+ y = _direction[1],
20617
+ z = _direction[2];
20618
+ var steps = degrees / 90; // Number of 90-degree steps
20619
+
20620
+ for (var i = 0; i < steps; i++) {
20621
+ var newX = x,
20622
+ newY = y,
20623
+ newZ = z;
20624
+ switch (axis) {
20625
+ case 'x':
20626
+ // Rotation around X-axis in Z-up system with flipped Y: Y becomes -Z, Z becomes Y
20627
+ newY = -z;
20628
+ newZ = y;
20629
+ break;
20630
+ case 'y':
20631
+ // Rotation around Y-axis in Z-up system with flipped Y: X becomes Z, Z becomes -X
20632
+ newX = z;
20633
+ newZ = -x;
20634
+ break;
20635
+ case 'z':
20636
+ // Rotation around Z-axis in Z-up system with flipped Y: X becomes -Y, Y becomes X
20637
+ newX = -y;
20638
+ newY = x;
20639
+ break;
20640
+ default:
20641
+ console.warn("\u26A0\uFE0F rotateDirectionVector: Invalid axis '".concat(axis, "'"));
20642
+ return direction;
20643
+ }
20644
+ x = newX;
20645
+ y = newY;
20646
+ z = newZ;
20647
+ }
20648
+ return [x, y, z];
20649
+ }
20650
+
20651
+ /**
20652
+ * Example usage in your rotate method:
20653
+ *
20654
+ * // After applying rotation to the component
20655
+ * component.rotation[axis] += radians
20656
+ *
20657
+ * // Update direction vectors for connectors
20658
+ * updateDirectionAfterRotation(component, axis, value)
20659
+ *
20660
+ * // Update matrices
20661
+ * component.updateMatrix()
20662
+ * component.updateMatrixWorld(true)
20663
+ */
20664
+
19876
20665
  var TransformOperationsManager = /*#__PURE__*/function () {
19877
20666
  /**
19878
20667
  * @param {Object} sceneViewer - The scene viewer instance
@@ -20564,16 +21353,9 @@ var TransformOperationsManager = /*#__PURE__*/function () {
20564
21353
  if (!component || degrees % 90 !== 0) {
20565
21354
  return;
20566
21355
  }
20567
- console.log("\uD83D\uDD04 Updating connector directions after ".concat(degrees, "\xB0 rotation around ").concat(axis, " axis"));
20568
21356
 
20569
- // Simple approach: just log that directions may need manual verification
20570
- // In most cases, Three.js world matrices handle the actual positioning correctly
20571
- component.traverse(function (child) {
20572
- var _child$userData5, _child$userData6;
20573
- if (((_child$userData5 = child.userData) === null || _child$userData5 === void 0 ? void 0 : _child$userData5.objectType) === 'connector' && (_child$userData6 = child.userData) !== null && _child$userData6 !== void 0 && _child$userData6.direction) {
20574
- console.log("\uD83D\uDCCD Connector ".concat(child.uuid, " direction may need verification after rotation"));
20575
- }
20576
- });
21357
+ // Use the direction utility to update direction vectors on Three.js connector objects
21358
+ updateDirectionAfterRotation(component, axis, degrees);
20577
21359
  }
20578
21360
 
20579
21361
  /**
@@ -20727,9 +21509,9 @@ var TransformOperationsManager = /*#__PURE__*/function () {
20727
21509
 
20728
21510
  // Traverse scene to find all connectors
20729
21511
  this.sceneViewer.scene.traverse(function (child) {
20730
- var _child$userData7;
21512
+ var _child$userData5;
20731
21513
  // Check if this is a connector (component connector or manual segment connector)
20732
- if (((_child$userData7 = child.userData) === null || _child$userData7 === void 0 ? void 0 : _child$userData7.objectType) === 'connector') {
21514
+ if (((_child$userData5 = child.userData) === null || _child$userData5 === void 0 ? void 0 : _child$userData5.objectType) === 'connector') {
20733
21515
  // Get world position of connector
20734
21516
  var connectorWorldPos = new THREE__namespace.Vector3();
20735
21517
  child.getWorldPosition(connectorWorldPos);
@@ -20780,8 +21562,8 @@ var TransformOperationsManager = /*#__PURE__*/function () {
20780
21562
  // Find all child connectors of the segment in the Three.js scene
20781
21563
  var connectors = [];
20782
21564
  segment.traverse(function (child) {
20783
- var _child$userData8;
20784
- if (((_child$userData8 = child.userData) === null || _child$userData8 === void 0 ? void 0 : _child$userData8.objectType) === 'segment-connector') {
21565
+ var _child$userData6;
21566
+ if (((_child$userData6 = child.userData) === null || _child$userData6 === void 0 ? void 0 : _child$userData6.objectType) === 'segment-connector') {
20785
21567
  connectors.push(child);
20786
21568
  }
20787
21569
  });
@@ -20845,17 +21627,17 @@ var TransformOperationsManager = /*#__PURE__*/function () {
20845
21627
 
20846
21628
  // Check all segments in the scene
20847
21629
  this.sceneViewer.scene.traverse(function (child) {
20848
- var _child$userData9, _child$userData0;
21630
+ var _child$userData7, _child$userData8;
20849
21631
  // Skip the segment itself
20850
21632
  if (child.uuid === segment.uuid) {
20851
21633
  return;
20852
21634
  }
20853
21635
 
20854
21636
  // Only check computed segments in the scene
20855
- if (((_child$userData9 = child.userData) === null || _child$userData9 === void 0 ? void 0 : _child$userData9.objectType) === 'segment' && (_child$userData0 = child.userData) !== null && _child$userData0 !== void 0 && _child$userData0.isDeclared) {
20856
- var _segment$userData6, _child$userData1, _segment$userData7, _child$userData10;
21637
+ if (((_child$userData7 = child.userData) === null || _child$userData7 === void 0 ? void 0 : _child$userData7.objectType) === 'segment' && (_child$userData8 = child.userData) !== null && _child$userData8 !== void 0 && _child$userData8.isDeclared) {
21638
+ var _segment$userData6, _child$userData9, _segment$userData7, _child$userData0;
20857
21639
  // Check if segments are part of the same connection path
20858
- var sameConnection = ((_segment$userData6 = segment.userData) === null || _segment$userData6 === void 0 ? void 0 : _segment$userData6.pathFrom) === ((_child$userData1 = child.userData) === null || _child$userData1 === void 0 ? void 0 : _child$userData1.pathFrom) && ((_segment$userData7 = segment.userData) === null || _segment$userData7 === void 0 ? void 0 : _segment$userData7.pathTo) === ((_child$userData10 = child.userData) === null || _child$userData10 === void 0 ? void 0 : _child$userData10.pathTo);
21640
+ var sameConnection = ((_segment$userData6 = segment.userData) === null || _segment$userData6 === void 0 ? void 0 : _segment$userData6.pathFrom) === ((_child$userData9 = child.userData) === null || _child$userData9 === void 0 ? void 0 : _child$userData9.pathFrom) && ((_segment$userData7 = segment.userData) === null || _segment$userData7 === void 0 ? void 0 : _segment$userData7.pathTo) === ((_child$userData0 = child.userData) === null || _child$userData0 === void 0 ? void 0 : _child$userData0.pathTo);
20859
21641
 
20860
21642
  // Get endpoints of the other segment (use stored endpoints if available)
20861
21643
  var otherEndpoints = _this2.getSegmentEndpoints(child);
@@ -21076,11 +21858,11 @@ var TransformOperationsManager = /*#__PURE__*/function () {
21076
21858
 
21077
21859
  // Traverse scene to find all component connectors
21078
21860
  this.sceneViewer.scene.traverse(function (child) {
21079
- var _child$userData11;
21861
+ var _child$userData1;
21080
21862
  if (collision) return; // Stop if collision already found
21081
21863
 
21082
21864
  // Check if this is a component connector (not a segment-connector)
21083
- if (((_child$userData11 = child.userData) === null || _child$userData11 === void 0 ? void 0 : _child$userData11.objectType) === 'connector') {
21865
+ if (((_child$userData1 = child.userData) === null || _child$userData1 === void 0 ? void 0 : _child$userData1.objectType) === 'connector') {
21084
21866
  var _segment$userData10, _segment$userData11;
21085
21867
  // Skip connectors that are connected to this segment (pathFrom or pathTo)
21086
21868
  var segmentPathFrom = (_segment$userData10 = segment.userData) === null || _segment$userData10 === void 0 ? void 0 : _segment$userData10.pathFrom;
@@ -21194,9 +21976,9 @@ var TransformOperationsManager = /*#__PURE__*/function () {
21194
21976
 
21195
21977
  // Traverse scene to find all component connectors
21196
21978
  this.sceneViewer.scene.traverse(function (child) {
21197
- var _child$userData12;
21979
+ var _child$userData10;
21198
21980
  // Check if this is a component connector (not a segment-connector)
21199
- if (((_child$userData12 = child.userData) === null || _child$userData12 === void 0 ? void 0 : _child$userData12.objectType) === 'connector') {
21981
+ if (((_child$userData10 = child.userData) === null || _child$userData10 === void 0 ? void 0 : _child$userData10.objectType) === 'connector') {
21200
21982
  // Get world position of connector
21201
21983
  var connectorWorldPos = new THREE__namespace.Vector3();
21202
21984
  child.getWorldPosition(connectorWorldPos);
@@ -21253,11 +22035,11 @@ var TransformOperationsManager = /*#__PURE__*/function () {
21253
22035
 
21254
22036
  // Traverse scene to find all components
21255
22037
  this.sceneViewer.scene.traverse(function (child) {
21256
- var _child$userData13, _child$userData14;
22038
+ var _child$userData11, _child$userData12;
21257
22039
  if (collision) return; // Stop if collision already found
21258
22040
 
21259
22041
  // Check if this is a component (equipment)
21260
- if (((_child$userData13 = child.userData) === null || _child$userData13 === void 0 ? void 0 : _child$userData13.objectType) === 'component' && (_child$userData14 = child.userData) !== null && _child$userData14 !== void 0 && _child$userData14.libraryId) {
22042
+ if (((_child$userData11 = child.userData) === null || _child$userData11 === void 0 ? void 0 : _child$userData11.objectType) === 'component' && (_child$userData12 = child.userData) !== null && _child$userData12 !== void 0 && _child$userData12.libraryId) {
21261
22043
  // Create bounding box for the component
21262
22044
  var componentBBox = new THREE__namespace.Box3().setFromObject(child);
21263
22045
 
@@ -21296,11 +22078,11 @@ var TransformOperationsManager = /*#__PURE__*/function () {
21296
22078
 
21297
22079
  // Traverse scene to find all manual segments (isDeclared === true)
21298
22080
  this.sceneViewer.scene.traverse(function (child) {
21299
- var _child$userData15, _child$userData16;
22081
+ var _child$userData13, _child$userData14;
21300
22082
  if (collision) return; // Stop if collision already found
21301
22083
 
21302
22084
  // Only check manual segments (isDeclared === true)
21303
- if (((_child$userData15 = child.userData) === null || _child$userData15 === void 0 ? void 0 : _child$userData15.objectType) === 'segment' && ((_child$userData16 = child.userData) === null || _child$userData16 === void 0 ? void 0 : _child$userData16.isDeclared) === true) {
22085
+ if (((_child$userData13 = child.userData) === null || _child$userData13 === void 0 ? void 0 : _child$userData13.objectType) === 'segment' && ((_child$userData14 = child.userData) === null || _child$userData14 === void 0 ? void 0 : _child$userData14.isDeclared) === true) {
21304
22086
  // Get segment endpoints
21305
22087
  var segmentEndpoints = _this3.getSegmentEndpoints(child);
21306
22088
 
@@ -21389,11 +22171,11 @@ var TransformOperationsManager = /*#__PURE__*/function () {
21389
22171
 
21390
22172
  // Traverse scene to find all manual segments (isDeclared === true)
21391
22173
  this.sceneViewer.scene.traverse(function (child) {
21392
- var _child$userData17, _child$userData18;
22174
+ var _child$userData15, _child$userData16;
21393
22175
  if (collision) return; // Stop if collision already found
21394
22176
 
21395
22177
  // Only check manual segments (isDeclared === true)
21396
- if (((_child$userData17 = child.userData) === null || _child$userData17 === void 0 ? void 0 : _child$userData17.objectType) === 'segment' && ((_child$userData18 = child.userData) === null || _child$userData18 === void 0 ? void 0 : _child$userData18.isDeclared) === true) {
22178
+ if (((_child$userData15 = child.userData) === null || _child$userData15 === void 0 ? void 0 : _child$userData15.objectType) === 'segment' && ((_child$userData16 = child.userData) === null || _child$userData16 === void 0 ? void 0 : _child$userData16.isDeclared) === true) {
21397
22179
  // Get segment endpoints
21398
22180
  var segmentEndpoints = _this4.getSegmentEndpoints(child);
21399
22181
 
@@ -21450,11 +22232,11 @@ var TransformOperationsManager = /*#__PURE__*/function () {
21450
22232
 
21451
22233
  // Traverse scene to find all components
21452
22234
  this.sceneViewer.scene.traverse(function (child) {
21453
- var _child$userData19, _child$userData20;
22235
+ var _child$userData17, _child$userData18;
21454
22236
  if (collision) return; // Stop if collision already found
21455
22237
 
21456
22238
  // Check if this is a component (equipment)
21457
- if (((_child$userData19 = child.userData) === null || _child$userData19 === void 0 ? void 0 : _child$userData19.objectType) === 'component' && (_child$userData20 = child.userData) !== null && _child$userData20 !== void 0 && _child$userData20.libraryId) {
22239
+ if (((_child$userData17 = child.userData) === null || _child$userData17 === void 0 ? void 0 : _child$userData17.objectType) === 'component' && (_child$userData18 = child.userData) !== null && _child$userData18 !== void 0 && _child$userData18.libraryId) {
21458
22240
  // Try to get worldBoundingBox from userData first (most up-to-date)
21459
22241
  var bbox = null;
21460
22242
  if (child.userData.worldBoundingBox) {
@@ -21599,8 +22381,8 @@ var TransformOperationsManager = /*#__PURE__*/function () {
21599
22381
  // Get the moved segment's connectors
21600
22382
  var movedConnectors = [];
21601
22383
  movedSegment.traverse(function (child) {
21602
- var _child$userData21;
21603
- if (((_child$userData21 = child.userData) === null || _child$userData21 === void 0 ? void 0 : _child$userData21.objectType) === 'segment-connector') {
22384
+ var _child$userData19;
22385
+ if (((_child$userData19 = child.userData) === null || _child$userData19 === void 0 ? void 0 : _child$userData19.objectType) === 'segment-connector') {
21604
22386
  movedConnectors.push(child);
21605
22387
  }
21606
22388
  });
@@ -21670,8 +22452,8 @@ var TransformOperationsManager = /*#__PURE__*/function () {
21670
22452
  // Get all connectors of the adjacent segment
21671
22453
  var adjacentConnectors = [];
21672
22454
  adjacentSegment.traverse(function (child) {
21673
- var _child$userData22;
21674
- if (((_child$userData22 = child.userData) === null || _child$userData22 === void 0 ? void 0 : _child$userData22.objectType) === 'segment-connector') {
22455
+ var _child$userData20;
22456
+ if (((_child$userData20 = child.userData) === null || _child$userData20 === void 0 ? void 0 : _child$userData20.objectType) === 'segment-connector') {
21675
22457
  adjacentConnectors.push(child);
21676
22458
  }
21677
22459
  });
@@ -21794,8 +22576,8 @@ var TransformOperationsManager = /*#__PURE__*/function () {
21794
22576
  // Also update child connectors' positions if they exist
21795
22577
  if (sceneDataComponent.children) {
21796
22578
  sceneDataComponent.children.forEach(function (child) {
21797
- var _child$userData23;
21798
- if (((_child$userData23 = child.userData) === null || _child$userData23 === void 0 ? void 0 : _child$userData23.objectType) === 'connector') {
22579
+ var _child$userData21;
22580
+ if (((_child$userData21 = child.userData) === null || _child$userData21 === void 0 ? void 0 : _child$userData21.objectType) === 'connector') {
21799
22581
  // Find the actual connector object in the scene
21800
22582
  var connectorObj = component.children.find(function (c) {
21801
22583
  var _c$userData;
@@ -21857,14 +22639,15 @@ var TransformOperationsManager = /*#__PURE__*/function () {
21857
22639
  // Also update child connectors' positions if they exist (rotation moves them in world space)
21858
22640
  if (sceneDataComponent.children) {
21859
22641
  sceneDataComponent.children.forEach(function (child) {
21860
- var _child$userData24;
21861
- if (((_child$userData24 = child.userData) === null || _child$userData24 === void 0 ? void 0 : _child$userData24.objectType) === 'connector') {
22642
+ var _child$userData22;
22643
+ if (((_child$userData22 = child.userData) === null || _child$userData22 === void 0 ? void 0 : _child$userData22.objectType) === 'connector') {
21862
22644
  // Find the actual connector object in the scene
21863
22645
  var connectorObj = component.children.find(function (c) {
21864
22646
  var _c$userData2;
21865
22647
  return c.uuid === child.uuid || ((_c$userData2 = c.userData) === null || _c$userData2 === void 0 ? void 0 : _c$userData2.originalUuid) === child.uuid;
21866
22648
  });
21867
22649
  if (connectorObj) {
22650
+ var _connectorObj$userDat;
21868
22651
  // Get world position of connector
21869
22652
  var worldPos = new THREE__namespace.Vector3();
21870
22653
  connectorObj.getWorldPosition(worldPos);
@@ -21875,6 +22658,12 @@ var TransformOperationsManager = /*#__PURE__*/function () {
21875
22658
  }
21876
22659
  child.userData.position = [cleanValue(worldPos.x), cleanValue(worldPos.y), cleanValue(worldPos.z)];
21877
22660
  console.log(" \u21B3 Updated child connector ".concat(child.uuid, " position to [").concat(child.userData.position.join(', '), "]"));
22661
+
22662
+ // Update connector's direction in scene data from the Three.js object
22663
+ if ((_connectorObj$userDat = connectorObj.userData) !== null && _connectorObj$userDat !== void 0 && _connectorObj$userDat.direction && Array.isArray(connectorObj.userData.direction)) {
22664
+ child.userData.direction = _toConsumableArray(connectorObj.userData.direction);
22665
+ console.log(" \u21B3 Updated child connector ".concat(child.uuid, " direction to [").concat(child.userData.direction.join(', '), "]"));
22666
+ }
21878
22667
  }
21879
22668
  }
21880
22669
  });
@@ -22187,8 +22976,8 @@ var TransformOperationsManager = /*#__PURE__*/function () {
22187
22976
  // Check if either external connector belongs to the active segment
22188
22977
  var activeSegmentConnectors = [];
22189
22978
  activeSegment.traverse(function (child) {
22190
- var _child$userData25;
22191
- if (((_child$userData25 = child.userData) === null || _child$userData25 === void 0 ? void 0 : _child$userData25.objectType) === 'segment-connector') {
22979
+ var _child$userData23;
22980
+ if (((_child$userData23 = child.userData) === null || _child$userData23 === void 0 ? void 0 : _child$userData23.objectType) === 'segment-connector') {
22192
22981
  activeSegmentConnectors.push(child.uuid);
22193
22982
  }
22194
22983
  });
@@ -22236,8 +23025,8 @@ var TransformOperationsManager = /*#__PURE__*/function () {
22236
23025
  // Get all connectors of the active segment
22237
23026
  var activeConnectors = [];
22238
23027
  activeSegment.traverse(function (child) {
22239
- var _child$userData26;
22240
- if (((_child$userData26 = child.userData) === null || _child$userData26 === void 0 ? void 0 : _child$userData26.objectType) === 'segment-connector') {
23028
+ var _child$userData24;
23029
+ if (((_child$userData24 = child.userData) === null || _child$userData24 === void 0 ? void 0 : _child$userData24.objectType) === 'segment-connector') {
22241
23030
  activeConnectors.push(child);
22242
23031
  }
22243
23032
  });
@@ -28065,6 +28854,22 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
28065
28854
  this.pathfinder = new pathfinder.Pathfinder(this.pathfinderConfig);
28066
28855
  this.sceneViewer.pathfinder = this.pathfinder;
28067
28856
 
28857
+ // ── Early exit if no connections ───────────────────────────────────
28858
+ // Skip expensive bounding box computation when there's nothing to connect
28859
+ if (!(!connections || connections.length === 0)) {
28860
+ _context.n = 1;
28861
+ break;
28862
+ }
28863
+ console.log("\u23ED\uFE0F Skipping pathfinding - no connections [".concat(context, "]"));
28864
+ return _context.a(2, {
28865
+ paths: [],
28866
+ gateways: [],
28867
+ rewiredConnections: [],
28868
+ metrics: {
28869
+ total: 0
28870
+ }
28871
+ });
28872
+ case 1:
28068
28873
  // ── Stage 1: Input Generation ──────────────────────────────────────
28069
28874
  inputGenStart = performance.now(); // Ensure all matrices are up to date before bounding box calculations (for all pathfinding executions)
28070
28875
  this.sceneViewer.scene.updateMatrixWorld(true);
@@ -30752,15 +31557,23 @@ var ModelManager = /*#__PURE__*/function () {
30752
31557
  if (child.userData) {
30753
31558
  clonedConnector.userData = _objectSpread2({}, child.userData);
30754
31559
 
30755
- // Deep copy critical data
31560
+ // Deep copy critical data - handle both array [x,y,z] and object {x,y,z} formats
30756
31561
  if (child.userData.worldBoundingBox) {
31562
+ var wbb = child.userData.worldBoundingBox;
30757
31563
  clonedConnector.userData.worldBoundingBox = {
30758
- min: _toConsumableArray(child.userData.worldBoundingBox.min),
30759
- max: _toConsumableArray(child.userData.worldBoundingBox.max)
31564
+ min: Array.isArray(wbb.min) ? _toConsumableArray(wbb.min) : _objectSpread2({}, wbb.min),
31565
+ max: Array.isArray(wbb.max) ? _toConsumableArray(wbb.max) : _objectSpread2({}, wbb.max)
30760
31566
  };
30761
31567
  }
30762
31568
  if (child.userData.direction) {
30763
- clonedConnector.userData.direction = _toConsumableArray(child.userData.direction);
31569
+ // Handle both array [x,y,z] and object {x,y,z} formats
31570
+ if (Array.isArray(child.userData.direction)) {
31571
+ clonedConnector.userData.direction = _toConsumableArray(child.userData.direction);
31572
+ } else if (_typeof(child.userData.direction) === 'object') {
31573
+ clonedConnector.userData.direction = _objectSpread2({}, child.userData.direction);
31574
+ } else {
31575
+ clonedConnector.userData.direction = child.userData.direction;
31576
+ }
30764
31577
  }
30765
31578
 
30766
31579
  // Set originalUuid to match the actual uuid (maintains consistency)
@@ -30791,6 +31604,7 @@ var ModelManager = /*#__PURE__*/function () {
30791
31604
  _context2.n = 1;
30792
31605
  break;
30793
31606
  }
31607
+ console.log("\uD83C\uDFAF GLB cache HIT: ".concat(modelKey));
30794
31608
  return _context2.a(2, gltfScene);
30795
31609
  case 1:
30796
31610
  // Check if preloading is in progress
@@ -30799,6 +31613,7 @@ var ModelManager = /*#__PURE__*/function () {
30799
31613
  _context2.n = 6;
30800
31614
  break;
30801
31615
  }
31616
+ console.log("\u23F3 Waiting for preloader: ".concat(modelKey));
30802
31617
  _context2.p = 2;
30803
31618
  _context2.n = 3;
30804
31619
  return modelPreloader.preloadingPromise;
@@ -30808,6 +31623,7 @@ var ModelManager = /*#__PURE__*/function () {
30808
31623
  _context2.n = 4;
30809
31624
  break;
30810
31625
  }
31626
+ console.log("\uD83C\uDFAF GLB cache HIT (after preload): ".concat(modelKey));
30811
31627
  return _context2.a(2, gltfScene);
30812
31628
  case 4:
30813
31629
  _context2.n = 6;
@@ -31135,7 +31951,7 @@ var ModelManager = /*#__PURE__*/function () {
31135
31951
  value: (function () {
31136
31952
  var _replaceWithGLBModels = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(libraryObjectsToReplace) {
31137
31953
  var _this3 = this;
31138
- var glbLoadingPromises;
31954
+ var startTime, glbLoadingPromises;
31139
31955
  return _regenerator().w(function (_context5) {
31140
31956
  while (1) switch (_context5.n) {
31141
31957
  case 0:
@@ -31145,23 +31961,29 @@ var ModelManager = /*#__PURE__*/function () {
31145
31961
  }
31146
31962
  return _context5.a(2);
31147
31963
  case 1:
31964
+ startTime = performance.now();
31148
31965
  console.log("\uD83D\uDD04 Replacing ".concat(libraryObjectsToReplace.length, " objects with GLB models..."));
31149
31966
  glbLoadingPromises = libraryObjectsToReplace.map(function (_ref, index) {
31150
31967
  var basicObject = _ref.basicObject,
31151
31968
  jsonData = _ref.jsonData,
31152
31969
  componentData = _ref.componentData;
31970
+ var modelStart = performance.now();
31153
31971
  return _this3.loadLibraryModel(basicObject, jsonData, componentData).then(function (libraryModel) {
31972
+ var _jsonData$userData;
31973
+ console.log("\u23F1\uFE0F GLB loaded: ".concat(((_jsonData$userData = jsonData.userData) === null || _jsonData$userData === void 0 ? void 0 : _jsonData$userData.libraryId) || jsonData.uuid, " in ").concat((performance.now() - modelStart).toFixed(0), "ms"));
31154
31974
  libraryObjectsToReplace[index].glbModel = libraryModel;
31155
31975
  return libraryModel;
31156
31976
  }).catch(function (error) {
31157
- var _jsonData$userData;
31158
- console.error("Failed to load ".concat((_jsonData$userData = jsonData.userData) === null || _jsonData$userData === void 0 ? void 0 : _jsonData$userData.libraryId, " GLB model:"), error);
31977
+ var _jsonData$userData2;
31978
+ console.error("Failed to load ".concat((_jsonData$userData2 = jsonData.userData) === null || _jsonData$userData2 === void 0 ? void 0 : _jsonData$userData2.libraryId, " GLB model:"), error);
31159
31979
  return basicObject;
31160
31980
  });
31161
31981
  });
31162
31982
  _context5.n = 2;
31163
31983
  return Promise.all(glbLoadingPromises);
31164
31984
  case 2:
31985
+ console.log("\u23F1\uFE0F All GLB models loaded in ".concat((performance.now() - startTime).toFixed(0), "ms"));
31986
+
31165
31987
  // Update world bounding boxes for loaded models and propagate to the live Three.js objects
31166
31988
  libraryObjectsToReplace.forEach(function (_ref2) {
31167
31989
  var jsonData = _ref2.jsonData,
@@ -32547,9 +33369,13 @@ var SceneOperationsManager = /*#__PURE__*/function () {
32547
33369
  value: (function () {
32548
33370
  var _loadSceneData = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(data) {
32549
33371
  var isImported,
33372
+ timers,
33373
+ totalStart,
33374
+ phaseStart,
32550
33375
  _yield$this$_createBa,
32551
33376
  crosscubeTextureSet,
32552
33377
  libraryObjectsToReplace,
33378
+ totalTime,
32553
33379
  _args4 = arguments,
32554
33380
  _t3;
32555
33381
  return _regenerator().w(function (_context4) {
@@ -32557,13 +33383,20 @@ var SceneOperationsManager = /*#__PURE__*/function () {
32557
33383
  case 0:
32558
33384
  isImported = _args4.length > 1 && _args4[1] !== undefined ? _args4[1] : true;
32559
33385
  this.sceneViewer;
33386
+ timers = {};
33387
+ totalStart = performance.now();
32560
33388
  _context4.p = 1;
32561
33389
  console.log("Loading scene data (".concat(isImported ? 'imported' : 'default', ")..."));
32562
33390
 
32563
33391
  // Phase 1: Prepare scene
33392
+ phaseStart = performance.now();
32564
33393
  _context4.n = 2;
32565
33394
  return this._prepareSceneForLoading(data, isImported);
32566
33395
  case 2:
33396
+ timers.phase1_prepare = performance.now() - phaseStart;
33397
+
33398
+ // Phase 2: Load dependencies and create basic objects
33399
+ phaseStart = performance.now();
32567
33400
  _context4.n = 3;
32568
33401
  return this._createBasicSceneObjects(data);
32569
33402
  case 3:
@@ -32573,17 +33406,33 @@ var SceneOperationsManager = /*#__PURE__*/function () {
32573
33406
  _yield$this$_createBa.materials;
32574
33407
  crosscubeTextureSet = _yield$this$_createBa.crosscubeTextureSet;
32575
33408
  libraryObjectsToReplace = _yield$this$_createBa.libraryObjectsToReplace;
33409
+ timers.phase2_createObjects = performance.now() - phaseStart;
33410
+
33411
+ // Phase 3: Replace basic objects with GLB models (moved before pathfinding)
33412
+ phaseStart = performance.now();
32576
33413
  _context4.n = 4;
32577
33414
  return this.modelManager.replaceWithGLBModels(libraryObjectsToReplace);
32578
33415
  case 4:
33416
+ timers.phase3_glbModels = performance.now() - phaseStart;
33417
+
33418
+ // Phase 4: Setup pathfinding (moved after GLB loading for consistent bounding boxes)
33419
+ phaseStart = performance.now();
32579
33420
  _context4.n = 5;
32580
33421
  return this._setupPathfinding(data, crosscubeTextureSet);
32581
33422
  case 5:
33423
+ timers.phase4_pathfinding = performance.now() - phaseStart;
33424
+
32582
33425
  // Phase 5: Finalize scene
33426
+ phaseStart = performance.now();
32583
33427
  this._finalizeScene(data, crosscubeTextureSet, isImported);
33428
+ timers.phase5_finalize = performance.now() - phaseStart;
32584
33429
 
32585
33430
  // Phase 6: Load behaviors (after GLB models are present so output objects can be resolved)
33431
+ phaseStart = performance.now();
32586
33432
  this._processBehaviors(data);
33433
+ timers.phase6_behaviors = performance.now() - phaseStart;
33434
+ totalTime = performance.now() - totalStart;
33435
+ console.log("\u23F1\uFE0F Scene Loading Performance:\n Phase 1 (Prepare) : ".concat(timers.phase1_prepare.toFixed(0), "ms\n Phase 2 (Create Objects): ").concat(timers.phase2_createObjects.toFixed(0), "ms\n Phase 3 (GLB Models) : ").concat(timers.phase3_glbModels.toFixed(0), "ms\n Phase 4 (Pathfinding) : ").concat(timers.phase4_pathfinding.toFixed(0), "ms\n Phase 5 (Finalize) : ").concat(timers.phase5_finalize.toFixed(0), "ms\n Phase 6 (Behaviors) : ").concat(timers.phase6_behaviors.toFixed(0), "ms\n \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n Total : ").concat(totalTime.toFixed(0), "ms"));
32587
33436
  console.log('✅ Scene loaded successfully');
32588
33437
 
32589
33438
  // Notify UI components (e.g. SceneHierarchy) that the scene is fully loaded
@@ -38089,7 +38938,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
38089
38938
  * Initialize the CentralPlant manager
38090
38939
  *
38091
38940
  * @constructor
38092
- * @version 0.3.16
38941
+ * @version 0.3.18
38093
38942
  * @updated 2025-10-22
38094
38943
  *
38095
38944
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.