fl-web-component 2.0.18 → 2.0.19-beta.0

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.
@@ -159,7 +159,7 @@ export class StreamLoader {
159
159
 
160
160
  async parseStreamImmediate(reader, list, range, abortSignal = null, streamId = null) {
161
161
  const batchMeshes = [];
162
- const batchPrimitives = [];
162
+ const batchPrimitiveGroups = [];
163
163
  const batchSize = this.batchSize;
164
164
  let batchStart = 0;
165
165
  const streamStats = {
@@ -168,8 +168,31 @@ export class StreamLoader {
168
168
  totalPrimitives: 0,
169
169
  };
170
170
 
171
- const processStreamBatch = async (meshesToProcess, primitivesToProcess) => {
171
+ const flattenPrimitiveGroups = primitiveGroups => {
172
+ if (!primitiveGroups || primitiveGroups.length === 0) return [];
173
+ const primitivesToProcess = [];
174
+ const usedIds = new Set();
175
+
176
+ primitiveGroups.forEach(group => {
177
+ const primitives = Array.isArray(group) ? group : [group];
178
+ primitives.forEach(primitive => {
179
+ if (!primitive) return;
180
+ if (primitive.id == null) {
181
+ primitivesToProcess.push(primitive);
182
+ return;
183
+ }
184
+ if (usedIds.has(primitive.id)) return;
185
+ usedIds.add(primitive.id);
186
+ primitivesToProcess.push(primitive);
187
+ });
188
+ });
189
+
190
+ return primitivesToProcess;
191
+ };
192
+
193
+ const processStreamBatch = async (meshesToProcess, primitiveGroupsToProcess) => {
172
194
  if (!meshesToProcess || meshesToProcess.length === 0) return;
195
+ const primitivesToProcess = flattenPrimitiveGroups(primitiveGroupsToProcess);
173
196
  await this.processBatchData(meshesToProcess, primitivesToProcess, list, range, abortSignal);
174
197
  streamStats.totalBatches += 1;
175
198
  streamStats.totalMeshes += meshesToProcess.length;
@@ -225,7 +248,7 @@ export class StreamLoader {
225
248
  const flushed = await this.workerRequest('streamFlush', { streamId: localStreamId });
226
249
  if (flushed?.meshes?.length) {
227
250
  batchMeshes.push(...flushed.meshes);
228
- batchPrimitives.push(...flushed.primitives);
251
+ batchPrimitiveGroups.push(...flushed.primitives);
229
252
  }
230
253
 
231
254
  while (batchMeshes.length - batchStart >= batchSize) {
@@ -234,9 +257,12 @@ export class StreamLoader {
234
257
  const interactionPromiseInBatch = ensureNotInteracting();
235
258
  if (interactionPromiseInBatch) await interactionPromiseInBatch;
236
259
  const meshesToProcess = batchMeshes.slice(batchStart, batchStart + batchSize);
237
- const primitivesToProcess = batchPrimitives.slice(batchStart, batchStart + batchSize);
260
+ const primitiveGroupsToProcess = batchPrimitiveGroups.slice(
261
+ batchStart,
262
+ batchStart + batchSize
263
+ );
238
264
  batchStart += batchSize;
239
- await processStreamBatch(meshesToProcess, primitivesToProcess);
265
+ await processStreamBatch(meshesToProcess, primitiveGroupsToProcess);
240
266
  }
241
267
 
242
268
  if (batchMeshes.length - batchStart > 0) {
@@ -245,9 +271,9 @@ export class StreamLoader {
245
271
  const interactionPromiseInBatch = ensureNotInteracting();
246
272
  if (interactionPromiseInBatch) await interactionPromiseInBatch;
247
273
  const meshesToProcess = batchMeshes.slice(batchStart);
248
- const primitivesToProcess = batchPrimitives.slice(batchStart);
274
+ const primitiveGroupsToProcess = batchPrimitiveGroups.slice(batchStart);
249
275
  batchStart = batchMeshes.length;
250
- await processStreamBatch(meshesToProcess, primitivesToProcess);
276
+ await processStreamBatch(meshesToProcess, primitiveGroupsToProcess);
251
277
  }
252
278
  break;
253
279
  }
@@ -266,7 +292,7 @@ export class StreamLoader {
266
292
 
267
293
  if (parsed?.meshes?.length) {
268
294
  batchMeshes.push(...parsed.meshes);
269
- batchPrimitives.push(...parsed.primitives);
295
+ batchPrimitiveGroups.push(...parsed.primitives);
270
296
  }
271
297
 
272
298
  while (batchMeshes.length - batchStart >= batchSize) {
@@ -275,14 +301,17 @@ export class StreamLoader {
275
301
  const interactionPromiseInBatch = ensureNotInteracting();
276
302
  if (interactionPromiseInBatch) await interactionPromiseInBatch;
277
303
  const meshesToProcess = batchMeshes.slice(batchStart, batchStart + batchSize);
278
- const primitivesToProcess = batchPrimitives.slice(batchStart, batchStart + batchSize);
304
+ const primitiveGroupsToProcess = batchPrimitiveGroups.slice(
305
+ batchStart,
306
+ batchStart + batchSize
307
+ );
279
308
  batchStart += batchSize;
280
- await processStreamBatch(meshesToProcess, primitivesToProcess);
309
+ await processStreamBatch(meshesToProcess, primitiveGroupsToProcess);
281
310
  }
282
311
 
283
312
  if (batchStart >= batchSize * 4) {
284
313
  batchMeshes.splice(0, batchStart);
285
- batchPrimitives.splice(0, batchStart);
314
+ batchPrimitiveGroups.splice(0, batchStart);
286
315
  batchStart = 0;
287
316
  }
288
317
  }
@@ -297,10 +326,127 @@ export class StreamLoader {
297
326
  const decoder = new TextDecoder('utf-8');
298
327
  let buffer = new Uint8Array();
299
328
 
300
- const pendingPrimitives = new Map();
301
- let expectingPrimitive = true;
329
+ let expectingMesh = true;
330
+ let pendingMesh = null;
331
+ let pendingPrimitiveIds = new Set();
332
+ let pendingPrimitiveGroups = [];
333
+ let pendingMatchedPrimitiveIds = new Set();
302
334
  let isFullProps = true;
303
335
 
336
+ const normalizeMesh = mesh => {
337
+ this._applyPrefixId(mesh, 'id');
338
+ const meshList = Array.isArray(mesh) ? mesh : [mesh];
339
+ meshList.forEach(item => {
340
+ if (!item) return;
341
+ if (!Array.isArray(item.primitives)) {
342
+ item.primitives = [];
343
+ }
344
+ this._applyPrefixId(item.primitives, 'prmid', item.documentId);
345
+ });
346
+ return mesh;
347
+ };
348
+
349
+ const collectMeshPrimitiveIds = mesh => {
350
+ const ids = [];
351
+ const meshList = Array.isArray(mesh) ? mesh : [mesh];
352
+ meshList.forEach(item => {
353
+ if (!item || !Array.isArray(item.primitives)) return;
354
+ item.primitives.forEach(primitive => {
355
+ if (primitive && primitive.prmid != null) {
356
+ ids.push(primitive.prmid);
357
+ }
358
+ });
359
+ });
360
+ return ids;
361
+ };
362
+
363
+ const isValidStreamMesh = mesh => {
364
+ const meshList = Array.isArray(mesh) ? mesh : [mesh];
365
+ if (meshList.length === 0) return false;
366
+ return meshList.every(
367
+ item => item && typeof item === 'object' && Array.isArray(item.primitives)
368
+ );
369
+ };
370
+
371
+ const tryParseMeshFromBuffer = () => {
372
+ if (buffer.length < 4) {
373
+ return { status: 'incomplete' };
374
+ }
375
+
376
+ const length = new DataView(buffer.buffer, buffer.byteOffset).getUint32(0, false);
377
+ if (length <= 0 || length > 10 * 1024 * 1024) {
378
+ return { status: 'invalid' };
379
+ }
380
+
381
+ const totalLen = 4 + length;
382
+ if (buffer.length < totalLen) {
383
+ return { status: 'incomplete' };
384
+ }
385
+
386
+ try {
387
+ const data = buffer.slice(4, totalLen);
388
+ const parsedMesh = JSON.parse(decoder.decode(data));
389
+ if (!isValidStreamMesh(parsedMesh)) {
390
+ return { status: 'invalid' };
391
+ }
392
+ const mesh = normalizeMesh(parsedMesh);
393
+ return { status: 'success', mesh, totalLen };
394
+ } catch (e) {
395
+ return { status: 'invalid' };
396
+ }
397
+ };
398
+
399
+ const parsePrimitiveFromBuffer = () => {
400
+ const dataView = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
401
+ const primitiveResult = this.parsePrimitive(dataView, buffer, 0, isFullProps);
402
+ const primitiveData = primitiveResult.primitive;
403
+
404
+ this._applyPrefixId(primitiveData, 'id');
405
+ this._applyPrefixId(primitiveData, 'material');
406
+
407
+ if (isFullProps) {
408
+ const { position, normal, posindex, nolindex, indices } =
409
+ this.parsePrimitiveData(primitiveData);
410
+ const formatted = this.formatPrimitiveData({ position, normal, posindex, nolindex });
411
+
412
+ delete primitiveData.nolindex;
413
+ delete primitiveData.posindex;
414
+
415
+ primitiveData.position = formatted.position;
416
+ primitiveData.normal = formatted.normal;
417
+ primitiveData.indices = indices;
418
+ }
419
+
420
+ return {
421
+ primitive: primitiveData,
422
+ consumedBytes: primitiveResult.offset,
423
+ };
424
+ };
425
+
426
+ const commitPendingMesh = async () => {
427
+ if (!pendingMesh) return;
428
+
429
+ const meshList = Array.isArray(pendingMesh) ? pendingMesh : [pendingMesh];
430
+ meshList.forEach(mesh => {
431
+ batchMeshes.push(mesh);
432
+ batchPrimitiveGroups.push(pendingPrimitiveGroups);
433
+ });
434
+
435
+ pendingMesh = null;
436
+ pendingPrimitiveIds = new Set();
437
+ pendingPrimitiveGroups = [];
438
+ pendingMatchedPrimitiveIds = new Set();
439
+
440
+ if (batchMeshes.length >= this.batchSize) {
441
+ await ensureNotAborted();
442
+ await this.ensureNotInteracting(abortSignal);
443
+ await processStreamBatch(batchMeshes, batchPrimitiveGroups);
444
+
445
+ batchMeshes.length = 0;
446
+ batchPrimitiveGroups.length = 0;
447
+ }
448
+ };
449
+
304
450
  while (true) {
305
451
  await ensureNotAborted();
306
452
 
@@ -319,11 +465,12 @@ export class StreamLoader {
319
465
  const { done, value } = content;
320
466
  if (done) {
321
467
  await ensureNotAborted();
468
+ await commitPendingMesh();
322
469
 
323
470
  if (batchMeshes.length > 0) {
324
471
  await ensureNotAborted();
325
472
  await this.ensureNotInteracting(abortSignal);
326
- await processStreamBatch(batchMeshes, batchPrimitives);
473
+ await processStreamBatch(batchMeshes, batchPrimitiveGroups);
327
474
  }
328
475
  break;
329
476
  }
@@ -335,103 +482,55 @@ export class StreamLoader {
335
482
 
336
483
  while (buffer.length > 0) {
337
484
  await ensureNotAborted();
338
- if (expectingPrimitive) {
339
- if (buffer.length < 12) break;
340
-
341
- try {
342
- const dataView = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
343
- const primitiveResult = this.parsePrimitive(dataView, buffer, 0, isFullProps);
344
- let primitiveData = primitiveResult.primitive;
345
-
346
- this._applyPrefixId(primitiveData, 'id');
347
- this._applyPrefixId(primitiveData, 'material');
348
- const consumedBytes = primitiveResult.offset;
349
-
350
- if (isFullProps) {
351
- const { position, normal, posindex, nolindex, indices } =
352
- this.parsePrimitiveData(primitiveData);
353
- const formatted = this.formatPrimitiveData({ position, normal, posindex, nolindex });
354
-
355
- delete primitiveData.nolindex;
356
- delete primitiveData.posindex;
357
-
358
- primitiveData.position = formatted.position;
359
- primitiveData.normal = formatted.normal;
360
- primitiveData.indices = indices;
361
- }
362
- const docId = primitiveData.id;
363
-
364
- pendingPrimitives.set(docId, primitiveData);
365
-
366
- expectingPrimitive = false;
367
- buffer = buffer.slice(consumedBytes);
368
- } catch (e) {
485
+ if (expectingMesh) {
486
+ const meshResult = tryParseMeshFromBuffer();
487
+ if (meshResult.status === 'incomplete') break;
488
+ if (meshResult.status === 'invalid') {
369
489
  break;
370
490
  }
371
- } else {
372
- if (buffer.length < 4) break;
373
491
 
374
- const length = new DataView(buffer.buffer, buffer.byteOffset).getUint32(0, false);
375
-
376
- if (length > 10 * 1024 * 1024) {
377
- expectingPrimitive = true;
492
+ await commitPendingMesh();
493
+ pendingMesh = meshResult.mesh;
494
+ pendingPrimitiveIds = new Set(collectMeshPrimitiveIds(pendingMesh));
495
+ pendingPrimitiveGroups = [];
496
+ pendingMatchedPrimitiveIds = new Set();
497
+ expectingMesh = false;
498
+ buffer = buffer.slice(meshResult.totalLen);
499
+ } else {
500
+ const nextMeshResult = tryParseMeshFromBuffer();
501
+ if (nextMeshResult.status === 'success') {
502
+ await commitPendingMesh();
503
+ pendingMesh = nextMeshResult.mesh;
504
+ pendingPrimitiveIds = new Set(collectMeshPrimitiveIds(pendingMesh));
505
+ expectingMesh = false;
506
+ buffer = buffer.slice(nextMeshResult.totalLen);
378
507
  continue;
379
508
  }
380
509
 
381
- const totalLen = 4 + length;
382
-
383
- if (buffer.length < totalLen) break;
384
-
385
- const data = buffer.slice(4, totalLen);
386
- let mesh = null;
510
+ if (buffer.length < 12) break;
387
511
 
512
+ let primitiveResult = null;
388
513
  try {
389
- const jsonStr = decoder.decode(data);
390
- mesh = JSON.parse(jsonStr);
514
+ primitiveResult = parsePrimitiveFromBuffer();
391
515
  } catch (e) {
392
- expectingPrimitive = true;
393
- continue;
516
+ break;
394
517
  }
395
518
 
396
- if (mesh) {
397
- isFullProps = true;
519
+ const primitiveData = primitiveResult.primitive;
398
520
 
399
- this._applyPrefixId(mesh, 'id');
400
- if (Array.isArray(mesh)) {
401
- mesh.forEach(item => {
402
- this._applyPrefixId(item.primitives, 'prmid', item.documentId);
403
- });
404
- } else {
405
- this._applyPrefixId(mesh.primitives, 'prmid', mesh.documentId);
406
- }
521
+ if (pendingPrimitiveIds.has(primitiveData.id)) {
522
+ pendingPrimitiveGroups.push(primitiveData);
523
+ pendingMatchedPrimitiveIds.add(primitiveData.id);
524
+ }
407
525
 
408
- let docId = mesh.id;
409
- const meshToPrimId = mesh.primitives.map(item => item.prmid);
410
- const hasPendingPrimitive = meshToPrimId.some(item => pendingPrimitives.has(item));
411
- const hasPendingMesh = pendingPrimitives.has(docId);
412
-
413
- if (hasPendingMesh || hasPendingPrimitive) {
414
- let primitiveData = pendingPrimitives.get(docId);
415
- if (!hasPendingMesh && hasPendingPrimitive) {
416
- const primId = meshToPrimId[0];
417
- primitiveData = pendingPrimitives.get(primId);
418
- }
419
-
420
- batchMeshes.push(mesh);
421
- batchPrimitives.push(primitiveData);
422
-
423
- if (batchMeshes.length >= this.batchSize) {
424
- await ensureNotAborted();
425
- await this.ensureNotInteracting(abortSignal);
426
- await processStreamBatch(batchMeshes, batchPrimitives);
427
-
428
- batchMeshes.length = 0;
429
- batchPrimitives.length = 0;
430
- }
431
- }
526
+ buffer = buffer.slice(primitiveResult.consumedBytes);
432
527
 
433
- expectingPrimitive = false;
434
- buffer = buffer.slice(totalLen);
528
+ if (
529
+ pendingPrimitiveIds.size > 0 &&
530
+ pendingMatchedPrimitiveIds.size >= pendingPrimitiveIds.size
531
+ ) {
532
+ await commitPendingMesh();
533
+ expectingMesh = true;
435
534
  }
436
535
  }
437
536
  }
@@ -448,10 +547,18 @@ export class StreamLoader {
448
547
  if (abortSignal && abortSignal.aborted) {
449
548
  throw new DOMException('Request was aborted', 'AbortError');
450
549
  }
451
- const renderResult = await this.renderModelData(meshes, primitives, list, range, null, undefined, {
452
- suppressLoadComplete: true,
453
- source: 'inRangeDis2',
454
- });
550
+ const renderResult = await this.renderModelData(
551
+ meshes,
552
+ primitives,
553
+ list,
554
+ range,
555
+ null,
556
+ undefined,
557
+ {
558
+ suppressLoadComplete: true,
559
+ source: 'inRangeDis2',
560
+ }
561
+ );
455
562
  if (renderResult && renderResult.canceled) {
456
563
  throw new DOMException('Batch loading was canceled', 'AbortError');
457
564
  }
@@ -849,7 +956,12 @@ export class StreamLoader {
849
956
  async fetchJsonStream(list, range, abortSignal = null, requestId = null) {
850
957
  try {
851
958
  const loadStartTime = Date.now();
852
- const streamResult = await this.fetchPrimitiveBufferByStream(range, list, abortSignal, requestId);
959
+ const streamResult = await this.fetchPrimitiveBufferByStream(
960
+ range,
961
+ list,
962
+ abortSignal,
963
+ requestId
964
+ );
853
965
  return {
854
966
  ...(streamResult || {}),
855
967
  duration: Date.now() - loadStartTime,
@@ -939,8 +1051,20 @@ export class StreamLoader {
939
1051
  if (dataView.byteLength < offset + 12) {
940
1052
  throw new Error('Insufficient data for primitive header');
941
1053
  }
942
- primitive.id = dataView.getInt32(offset, false);
1054
+ const primitiveIdTextLen = dataView.getUint32(offset, false);
943
1055
  offset += 4;
1056
+ if (primitiveIdTextLen === 0xffffffff) {
1057
+ primitive.id = null;
1058
+ } else if (primitiveIdTextLen > 0) {
1059
+ if (dataView.byteLength < offset + primitiveIdTextLen) {
1060
+ throw new Error('Insufficient data for primitive id content');
1061
+ }
1062
+ const textBytes = uint8Array.subarray(offset, offset + primitiveIdTextLen);
1063
+ primitive.id = new TextDecoder('utf-8').decode(textBytes);
1064
+ offset += primitiveIdTextLen;
1065
+ } else {
1066
+ primitive.id = '';
1067
+ }
944
1068
 
945
1069
  if (dataView.byteLength < offset + 4) {
946
1070
  throw new Error('Insufficient data for GeomText length');
@@ -960,8 +1084,23 @@ export class StreamLoader {
960
1084
  primitive.documentId = '';
961
1085
  }
962
1086
 
963
- primitive.material = dataView.getInt32(offset, false);
1087
+ if (dataView.byteLength < offset + 4) {
1088
+ throw new Error('Insufficient data for Material length');
1089
+ }
1090
+ const materialTextLen = dataView.getUint32(offset, false);
964
1091
  offset += 4;
1092
+ if (materialTextLen === 0xffffffff) {
1093
+ primitive.material = null;
1094
+ } else if (materialTextLen > 0) {
1095
+ if (dataView.byteLength < offset + materialTextLen) {
1096
+ throw new Error('Insufficient data for Material content');
1097
+ }
1098
+ const textBytes = uint8Array.subarray(offset, offset + materialTextLen);
1099
+ primitive.material = new TextDecoder('utf-8').decode(textBytes);
1100
+ offset += materialTextLen;
1101
+ } else {
1102
+ primitive.material = '';
1103
+ }
965
1104
 
966
1105
  if (dataView.byteLength < offset + 4) {
967
1106
  throw new Error('Insufficient data for GeomText length');
@@ -1311,10 +1450,10 @@ export class StreamLoader {
1311
1450
  }
1312
1451
  }
1313
1452
 
1314
- async getBox({ id, projectId = this.projectId || 0 }) {
1453
+ async getBox({ id, projectId = this.projectId || 0, isDebug }) {
1315
1454
  // 1. 尝试从 IndexedDB 读取
1316
1455
  const cached = await this._getFromDB(id, projectId);
1317
- if (cached) {
1456
+ if (cached && !isDebug) {
1318
1457
  return cached;
1319
1458
  }
1320
1459
 
@@ -1539,7 +1678,11 @@ export class StreamLoader {
1539
1678
  this.sceneBox = sceneBox;
1540
1679
 
1541
1680
  // 获取BoxIndex
1542
- const boxIndex = await this.getBox({ id: item.id, projectId: this.projectId });
1681
+ const boxIndex = await this.getBox({
1682
+ id: item.id,
1683
+ projectId: this.projectId,
1684
+ isDebug: this.debug,
1685
+ });
1543
1686
  this.boxIndex = boxIndex;
1544
1687
 
1545
1688
  // 将 mergeMaterialData 覆盖到 materialData(按 id 覆盖 color/transp)