@bldrs-ai/conway 1.413.1255 → 1.417.1262
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.
- package/compiled/examples/browser-bundled.cjs +1 -1
- package/compiled/examples/cli-bundled.cjs +194 -227
- package/compiled/examples/cli-step-bundled.cjs +1 -1
- package/compiled/examples/validator-bundled.cjs +1 -1
- package/compiled/src/compat/web-ifc/ifc_api.d.ts +20 -0
- package/compiled/src/compat/web-ifc/ifc_api.d.ts.map +1 -1
- package/compiled/src/compat/web-ifc/ifc_api.js +29 -0
- package/compiled/src/compat/web-ifc/ifc_api_model_passthrough_factory.d.ts +16 -0
- package/compiled/src/compat/web-ifc/ifc_api_model_passthrough_factory.d.ts.map +1 -1
- package/compiled/src/compat/web-ifc/ifc_api_model_passthrough_factory.js +29 -0
- package/compiled/src/compat/web-ifc/ifc_api_proxy_ifc.d.ts +40 -0
- package/compiled/src/compat/web-ifc/ifc_api_proxy_ifc.d.ts.map +1 -1
- package/compiled/src/compat/web-ifc/ifc_api_proxy_ifc.js +106 -0
- package/compiled/src/compat/web-ifc/ifc_api_streamed_open.test.d.ts +2 -0
- package/compiled/src/compat/web-ifc/ifc_api_streamed_open.test.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/ifc_api_streamed_open.test.js +100 -0
- package/compiled/src/ifc/ifc_demand_extraction.test.d.ts +2 -0
- package/compiled/src/ifc/ifc_demand_extraction.test.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_demand_extraction.test.js +111 -0
- package/compiled/src/ifc/ifc_geometry_extraction.d.ts +47 -0
- package/compiled/src/ifc/ifc_geometry_extraction.d.ts.map +1 -1
- package/compiled/src/ifc/ifc_geometry_extraction.js +223 -269
- package/compiled/src/ifc/ifc_stream_open.d.ts +68 -0
- package/compiled/src/ifc/ifc_stream_open.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_stream_open.js +46 -0
- package/compiled/src/ifc/ifc_stream_open.test.d.ts +2 -0
- package/compiled/src/ifc/ifc_stream_open.test.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_stream_open.test.js +63 -0
- package/compiled/src/ifc/ifc_tile_extractor.d.ts +84 -0
- package/compiled/src/ifc/ifc_tile_extractor.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_tile_extractor.js +134 -0
- package/compiled/src/ifc/ifc_tile_extractor.test.d.ts +2 -0
- package/compiled/src/ifc/ifc_tile_extractor.test.d.ts.map +1 -0
- package/compiled/src/ifc/ifc_tile_extractor.test.js +118 -0
- package/compiled/src/index.d.ts +14 -0
- package/compiled/src/index.d.ts.map +1 -1
- package/compiled/src/index.js +16 -0
- package/compiled/src/version/version.js +1 -1
- package/compiled/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -139,6 +139,9 @@ export class IfcGeometryExtraction {
|
|
|
139
139
|
this.geometryTypeCounts = new Map();
|
|
140
140
|
this.csgDepth = 0;
|
|
141
141
|
this.freeVectorPolygonalFaces_ = [];
|
|
142
|
+
// Guards prepareExtractionMaps_ so the demand path and the whole-model
|
|
143
|
+
// walk can both trigger it without duplicating work.
|
|
144
|
+
this.extractionMapsPrepared_ = false;
|
|
142
145
|
this.csgMemoization = !this.lowMemoryMode;
|
|
143
146
|
this.materials = model.materials;
|
|
144
147
|
this.scene = new IfcSceneBuilder(model, conwayModel, this.materials);
|
|
@@ -4364,6 +4367,223 @@ export class IfcGeometryExtraction {
|
|
|
4364
4367
|
fs.writeFileSync(outputFilePath, objContent, 'utf8')
|
|
4365
4368
|
} */
|
|
4366
4369
|
}
|
|
4370
|
+
/**
|
|
4371
|
+
* One-time extraction setup shared by the whole-model walk and the
|
|
4372
|
+
* per-product demand path (Phase B2): linear scaling factor and the
|
|
4373
|
+
* cross-product maps (materials, material definitions, rel-voids, styled
|
|
4374
|
+
* items). Idempotent.
|
|
4375
|
+
*/
|
|
4376
|
+
prepareExtractionMaps_() {
|
|
4377
|
+
if (this.extractionMapsPrepared_) {
|
|
4378
|
+
return;
|
|
4379
|
+
}
|
|
4380
|
+
this.extractionMapsPrepared_ = true;
|
|
4381
|
+
this.extractLinearScalingFactor();
|
|
4382
|
+
// populate relMaterialsMap
|
|
4383
|
+
const relAssociatesMaterials = this.model.types(IfcRelAssociatesMaterial);
|
|
4384
|
+
for (const relAssociateMaterial of relAssociatesMaterials) {
|
|
4385
|
+
const relatingMaterial = relAssociateMaterial.RelatingMaterial;
|
|
4386
|
+
try {
|
|
4387
|
+
const relatedObjects = relAssociateMaterial.RelatedObjects;
|
|
4388
|
+
for (const relatedObject of relatedObjects) {
|
|
4389
|
+
const product = relatedObject;
|
|
4390
|
+
if (product instanceof IfcProduct) {
|
|
4391
|
+
if (product instanceof IfcOpeningElement ||
|
|
4392
|
+
product instanceof IfcSpace ||
|
|
4393
|
+
product instanceof IfcOpeningStandardCase) {
|
|
4394
|
+
continue;
|
|
4395
|
+
}
|
|
4396
|
+
// save mapping of IfcProduct --> IfcMaterial
|
|
4397
|
+
this.materials.relMaterialsMap.set(product.localID, relatingMaterial.localID);
|
|
4398
|
+
}
|
|
4399
|
+
else {
|
|
4400
|
+
// Logger.warning(`type other than IfcProduct: ${EntityTypesIfc[product.type]}`)
|
|
4401
|
+
}
|
|
4402
|
+
}
|
|
4403
|
+
}
|
|
4404
|
+
catch (ex) {
|
|
4405
|
+
if (ex instanceof Error) {
|
|
4406
|
+
if (MATERIAL_RELATED_OBJECTS_PERMISSIVE) {
|
|
4407
|
+
Logger.error("Error processing relatingMaterial expressID: " +
|
|
4408
|
+
`${relatingMaterial.expressID}, error: ${ex.message}`);
|
|
4409
|
+
}
|
|
4410
|
+
else {
|
|
4411
|
+
throw ex;
|
|
4412
|
+
}
|
|
4413
|
+
}
|
|
4414
|
+
else {
|
|
4415
|
+
Logger.error("Unknown exception processing IfcRelAssociateMaterials.");
|
|
4416
|
+
}
|
|
4417
|
+
}
|
|
4418
|
+
}
|
|
4419
|
+
// populate MaterialDefinitionsMap
|
|
4420
|
+
this.populateMaterialDefinitionsMap();
|
|
4421
|
+
// populate relvoids map
|
|
4422
|
+
this.populateRelVoidsMap();
|
|
4423
|
+
// populate styled items map
|
|
4424
|
+
this.populateStyledItemsMap();
|
|
4425
|
+
}
|
|
4426
|
+
/**
|
|
4427
|
+
* Prepare for per-product demand extraction (Phase B2) without running the
|
|
4428
|
+
* whole-model walk: populates the shared extraction maps so
|
|
4429
|
+
* {@link extractProductGeometryByLocalID} can extract any single product.
|
|
4430
|
+
* Idempotent; the whole-model walk shares the same preparation.
|
|
4431
|
+
*/
|
|
4432
|
+
prepareDemandExtraction() {
|
|
4433
|
+
this.prepareExtractionMaps_();
|
|
4434
|
+
}
|
|
4435
|
+
/**
|
|
4436
|
+
* Extract one product's geometry into the scene by local ID — the demand
|
|
4437
|
+
* path's entry (Phase B2). Requires {@link prepareDemandExtraction} (or a
|
|
4438
|
+
* prior whole-model walk) so the shared maps exist.
|
|
4439
|
+
*
|
|
4440
|
+
* Note: products that are targets of IfcRelAggregates whose relating
|
|
4441
|
+
* object carries rel-voids get those master voids only on the whole-model
|
|
4442
|
+
* walk; the demand path extracts the product's own voids. The parity gate
|
|
4443
|
+
* for this seam is the whole-model digest CI.
|
|
4444
|
+
*
|
|
4445
|
+
* @param localID The product's local ID.
|
|
4446
|
+
* @return {boolean} True if the local ID was an IfcProduct and was
|
|
4447
|
+
* extracted; false if it wasn't a product.
|
|
4448
|
+
*/
|
|
4449
|
+
extractProductGeometryByLocalID(localID) {
|
|
4450
|
+
this.prepareExtractionMaps_();
|
|
4451
|
+
const element = this.model.getElementByLocalID(localID);
|
|
4452
|
+
if (!(element instanceof IfcProduct)) {
|
|
4453
|
+
return false;
|
|
4454
|
+
}
|
|
4455
|
+
this.extractProductGeometry(element);
|
|
4456
|
+
return true;
|
|
4457
|
+
}
|
|
4458
|
+
/**
|
|
4459
|
+
* Extract one product's geometry into the scene: placement, material /
|
|
4460
|
+
* style extraction, representation items (including mapped items) and
|
|
4461
|
+
* rel-void application. The deduplicated body of the whole-model walk's
|
|
4462
|
+
* two product loops (plain products + rel-aggregates), shared with the
|
|
4463
|
+
* per-product demand path.
|
|
4464
|
+
*
|
|
4465
|
+
* @param product The product to extract.
|
|
4466
|
+
* @param precomputedRelVoids Optional caller-owned rel-voids (the
|
|
4467
|
+
* rel-aggregates "master" case): when supplied the caller retains
|
|
4468
|
+
* ownership and this method does not delete the mesh vector; when
|
|
4469
|
+
* omitted, voids are extracted per product and freed here.
|
|
4470
|
+
*/
|
|
4471
|
+
extractProductGeometry(product, precomputedRelVoids) {
|
|
4472
|
+
this.scene.clearParentStack();
|
|
4473
|
+
const isSpace = product instanceof IfcOpeningElement ||
|
|
4474
|
+
product instanceof IfcSpace ||
|
|
4475
|
+
product instanceof IfcOpeningStandardCase;
|
|
4476
|
+
const objectPlacement = product.ObjectPlacement;
|
|
4477
|
+
if (objectPlacement !== null) {
|
|
4478
|
+
this.extractPlacement(objectPlacement);
|
|
4479
|
+
}
|
|
4480
|
+
const representations = product.Representation;
|
|
4481
|
+
if (representations === null) {
|
|
4482
|
+
return;
|
|
4483
|
+
}
|
|
4484
|
+
// extract styledItem material
|
|
4485
|
+
const styledItemID = this.extractMaterialStyle(product);
|
|
4486
|
+
let hasRelVoid = false;
|
|
4487
|
+
const extractRelVoidsResult = precomputedRelVoids ?? this.extractRelVoids(product);
|
|
4488
|
+
let relVoidsMeshVector;
|
|
4489
|
+
let relVoidLocalIDs;
|
|
4490
|
+
if (extractRelVoidsResult !== void 0) {
|
|
4491
|
+
[relVoidsMeshVector, relVoidLocalIDs] = extractRelVoidsResult;
|
|
4492
|
+
}
|
|
4493
|
+
if (relVoidsMeshVector !== void 0) {
|
|
4494
|
+
hasRelVoid = true;
|
|
4495
|
+
}
|
|
4496
|
+
if (styledItemID !== void 0) {
|
|
4497
|
+
// optimization: extract the first representation item and cache
|
|
4498
|
+
// the styleID to apply to the rest of the product geometry
|
|
4499
|
+
const styledItem = this.model.getElementByLocalID(styledItemID);
|
|
4500
|
+
let reusableStyleID;
|
|
4501
|
+
for (const representation of representations.Representations) {
|
|
4502
|
+
if (representation instanceof IfcShapeRepresentation) {
|
|
4503
|
+
// this check is essential -
|
|
4504
|
+
// if RepresentationIdentifier !== Body or Facetation we must skip it
|
|
4505
|
+
if (representation.RepresentationIdentifier !== "Body" &&
|
|
4506
|
+
representation.RepresentationIdentifier !== "Facetation") {
|
|
4507
|
+
continue;
|
|
4508
|
+
}
|
|
4509
|
+
}
|
|
4510
|
+
for (const item of representation.Items) {
|
|
4511
|
+
if (item instanceof IfcMappedItem) {
|
|
4512
|
+
this.extractMappedItem(item, product, hasRelVoid, isSpace);
|
|
4513
|
+
}
|
|
4514
|
+
else {
|
|
4515
|
+
try {
|
|
4516
|
+
this.extractRepresentationItem(item, product.localID, hasRelVoid, isSpace);
|
|
4517
|
+
if (hasRelVoid) {
|
|
4518
|
+
this.applyRelVoidToRepresentation(item, relVoidsMeshVector, product.localID, relVoidLocalIDs, void 0, isSpace);
|
|
4519
|
+
}
|
|
4520
|
+
}
|
|
4521
|
+
catch (error) {
|
|
4522
|
+
if (error instanceof Error) {
|
|
4523
|
+
Logger.error(`Error extracting representation item\n\t${error.message}\n\tExpress ID: #${item.expressID}`);
|
|
4524
|
+
}
|
|
4525
|
+
else {
|
|
4526
|
+
Logger.error(`Error extracting representation item\n\tExpress ID: #${item.expressID}`);
|
|
4527
|
+
}
|
|
4528
|
+
}
|
|
4529
|
+
}
|
|
4530
|
+
const styledItemLocalID_ = this.materials.styledItemMap.get(item.localID);
|
|
4531
|
+
if (styledItemLocalID_ !== undefined) {
|
|
4532
|
+
const styledItem_ = this.model.getElementByLocalID(styledItemLocalID_);
|
|
4533
|
+
this.extractStyledItem(styledItem_);
|
|
4534
|
+
}
|
|
4535
|
+
else if (reusableStyleID !== void 0) {
|
|
4536
|
+
this.materials.addGeometryMapping(item.localID, reusableStyleID);
|
|
4537
|
+
}
|
|
4538
|
+
else if (styledItem instanceof IfcStyledItem) {
|
|
4539
|
+
// here we have the styled item, apply it to all geometry in this IfcProduct
|
|
4540
|
+
reusableStyleID = this.extractStyledItem(styledItem, item);
|
|
4541
|
+
}
|
|
4542
|
+
}
|
|
4543
|
+
}
|
|
4544
|
+
}
|
|
4545
|
+
else {
|
|
4546
|
+
for (const representation of representations.Representations) {
|
|
4547
|
+
if (representation instanceof IfcShapeRepresentation) {
|
|
4548
|
+
// this check is essential -
|
|
4549
|
+
// if RepresentationIdentifier !== Body or Facetation we must skip it
|
|
4550
|
+
if (representation.RepresentationIdentifier !== "Body" &&
|
|
4551
|
+
representation.RepresentationIdentifier !== "Facetation") {
|
|
4552
|
+
continue;
|
|
4553
|
+
}
|
|
4554
|
+
}
|
|
4555
|
+
for (const item of representation.Items) {
|
|
4556
|
+
if (item instanceof IfcMappedItem) {
|
|
4557
|
+
this.extractMappedItem(item, product, hasRelVoid, isSpace);
|
|
4558
|
+
}
|
|
4559
|
+
else {
|
|
4560
|
+
try {
|
|
4561
|
+
this.extractRepresentationItem(item, product.localID, hasRelVoid, isSpace);
|
|
4562
|
+
if (hasRelVoid) {
|
|
4563
|
+
this.applyRelVoidToRepresentation(item, relVoidsMeshVector, product.localID, relVoidLocalIDs, void 0, isSpace);
|
|
4564
|
+
}
|
|
4565
|
+
const styledItemLocalID_ = this.materials.styledItemMap.get(item.localID);
|
|
4566
|
+
if (styledItemLocalID_ !== void 0) {
|
|
4567
|
+
const styledItem_ = this.model.getElementByLocalID(styledItemLocalID_);
|
|
4568
|
+
this.extractStyledItem(styledItem_);
|
|
4569
|
+
}
|
|
4570
|
+
}
|
|
4571
|
+
catch (error) {
|
|
4572
|
+
if (error instanceof Error) {
|
|
4573
|
+
Logger.error(`Error extracting representation item\n\t${error.message}\n\t${error.stack}\n\tExpress ID: #${item.localID}`);
|
|
4574
|
+
}
|
|
4575
|
+
else {
|
|
4576
|
+
Logger.error(`Unknown error extracting representation item Express ID: #${item.localID}`);
|
|
4577
|
+
}
|
|
4578
|
+
}
|
|
4579
|
+
}
|
|
4580
|
+
}
|
|
4581
|
+
}
|
|
4582
|
+
}
|
|
4583
|
+
if (precomputedRelVoids === void 0) {
|
|
4584
|
+
relVoidsMeshVector?.delete();
|
|
4585
|
+
}
|
|
4586
|
+
}
|
|
4367
4587
|
/**
|
|
4368
4588
|
* Extract the geometry data from the IFC
|
|
4369
4589
|
*
|
|
@@ -4444,10 +4664,7 @@ export class IfcGeometryExtraction {
|
|
|
4444
4664
|
*/
|
|
4445
4665
|
*extractIFCGeometryDataIncremental() {
|
|
4446
4666
|
let result = ExtractResult.INCOMPLETE;
|
|
4447
|
-
this.extractLinearScalingFactor();
|
|
4448
4667
|
const previousMemoizationState = this.model.elementMemoization;
|
|
4449
|
-
// populate relMaterialsMap
|
|
4450
|
-
const relAssociatesMaterials = this.model.types(IfcRelAssociatesMaterial);
|
|
4451
4668
|
try {
|
|
4452
4669
|
// 256 meg limit for memoization - smaller models get a big
|
|
4453
4670
|
// win from memoization, but much larger models it uses far too much heap.
|
|
@@ -4456,47 +4673,7 @@ export class IfcGeometryExtraction {
|
|
|
4456
4673
|
if (this.lowMemoryMode || this.model.bufferBytesize > MEMOIZATION_THRESHOLD) {
|
|
4457
4674
|
this.model.elementMemoization = false;
|
|
4458
4675
|
}
|
|
4459
|
-
|
|
4460
|
-
const relatingMaterial = relAssociateMaterial.RelatingMaterial;
|
|
4461
|
-
try {
|
|
4462
|
-
const relatedObjects = relAssociateMaterial.RelatedObjects;
|
|
4463
|
-
for (const relatedObject of relatedObjects) {
|
|
4464
|
-
const product = relatedObject;
|
|
4465
|
-
if (product instanceof IfcProduct) {
|
|
4466
|
-
if (product instanceof IfcOpeningElement ||
|
|
4467
|
-
product instanceof IfcSpace ||
|
|
4468
|
-
product instanceof IfcOpeningStandardCase) {
|
|
4469
|
-
continue;
|
|
4470
|
-
}
|
|
4471
|
-
// save mapping of IfcProduct --> IfcMaterial
|
|
4472
|
-
this.materials.relMaterialsMap.set(product.localID, relatingMaterial.localID);
|
|
4473
|
-
}
|
|
4474
|
-
else {
|
|
4475
|
-
// Logger.warning(`type other than IfcProduct: ${EntityTypesIfc[product.type]}`)
|
|
4476
|
-
}
|
|
4477
|
-
}
|
|
4478
|
-
}
|
|
4479
|
-
catch (ex) {
|
|
4480
|
-
if (ex instanceof Error) {
|
|
4481
|
-
if (MATERIAL_RELATED_OBJECTS_PERMISSIVE) {
|
|
4482
|
-
Logger.error("Error processing relatingMaterial expressID: " +
|
|
4483
|
-
`${relatingMaterial.expressID}, error: ${ex.message}`);
|
|
4484
|
-
}
|
|
4485
|
-
else {
|
|
4486
|
-
throw ex;
|
|
4487
|
-
}
|
|
4488
|
-
}
|
|
4489
|
-
else {
|
|
4490
|
-
Logger.error("Unknown exception processing IfcRelAssociateMaterials.");
|
|
4491
|
-
}
|
|
4492
|
-
}
|
|
4493
|
-
}
|
|
4494
|
-
// populate MaterialDefinitionsMap
|
|
4495
|
-
this.populateMaterialDefinitionsMap();
|
|
4496
|
-
// populate relvoids map
|
|
4497
|
-
this.populateRelVoidsMap();
|
|
4498
|
-
// populate styled items map
|
|
4499
|
-
this.populateStyledItemsMap();
|
|
4676
|
+
this.prepareExtractionMaps_();
|
|
4500
4677
|
const products = this.model.types(IfcProduct);
|
|
4501
4678
|
// Prefix-sum counts (no iteration/materialization) — see typeCount.
|
|
4502
4679
|
// Slight over-count from multi-mapped elements just leaves the bar
|
|
@@ -4505,117 +4682,7 @@ export class IfcGeometryExtraction {
|
|
|
4505
4682
|
let completedItems = 0;
|
|
4506
4683
|
for (const product of products) {
|
|
4507
4684
|
yield [++completedItems, totalItems];
|
|
4508
|
-
this.
|
|
4509
|
-
const isSpace = product instanceof IfcOpeningElement ||
|
|
4510
|
-
product instanceof IfcSpace ||
|
|
4511
|
-
product instanceof IfcOpeningStandardCase;
|
|
4512
|
-
const objectPlacement = product.ObjectPlacement;
|
|
4513
|
-
if (objectPlacement !== null) {
|
|
4514
|
-
this.extractPlacement(objectPlacement);
|
|
4515
|
-
}
|
|
4516
|
-
const representations = product.Representation;
|
|
4517
|
-
if (representations !== null) {
|
|
4518
|
-
// extract styledItem material
|
|
4519
|
-
const styledItemID = this.extractMaterialStyle(product);
|
|
4520
|
-
let hasRelVoid = false;
|
|
4521
|
-
const extractRelVoidsResult = this.extractRelVoids(product);
|
|
4522
|
-
let relVoidsMeshVector;
|
|
4523
|
-
let relVoidLocalIDs;
|
|
4524
|
-
if (extractRelVoidsResult !== void 0) {
|
|
4525
|
-
[relVoidsMeshVector, relVoidLocalIDs] = extractRelVoidsResult;
|
|
4526
|
-
}
|
|
4527
|
-
if (relVoidsMeshVector !== void 0) {
|
|
4528
|
-
hasRelVoid = true;
|
|
4529
|
-
}
|
|
4530
|
-
if (styledItemID !== void 0) {
|
|
4531
|
-
// optimization: extract the first representation item and cache
|
|
4532
|
-
// the styleID to apply to the rest of the product geometry
|
|
4533
|
-
const styledItem = this.model.getElementByLocalID(styledItemID);
|
|
4534
|
-
let reusableStyleID;
|
|
4535
|
-
for (const representation of representations.Representations) {
|
|
4536
|
-
if (representation instanceof IfcShapeRepresentation) {
|
|
4537
|
-
// this check is essential -
|
|
4538
|
-
// if RepresentationIdentifier !== Body or Facetation we must skip it
|
|
4539
|
-
if (representation.RepresentationIdentifier !== "Body" &&
|
|
4540
|
-
representation.RepresentationIdentifier !== "Facetation") {
|
|
4541
|
-
continue;
|
|
4542
|
-
}
|
|
4543
|
-
}
|
|
4544
|
-
for (const item of representation.Items) {
|
|
4545
|
-
if (item instanceof IfcMappedItem) {
|
|
4546
|
-
this.extractMappedItem(item, product, hasRelVoid, isSpace);
|
|
4547
|
-
}
|
|
4548
|
-
else {
|
|
4549
|
-
try {
|
|
4550
|
-
this.extractRepresentationItem(item, product.localID, hasRelVoid, isSpace);
|
|
4551
|
-
if (hasRelVoid) {
|
|
4552
|
-
this.applyRelVoidToRepresentation(item, relVoidsMeshVector, product.localID, relVoidLocalIDs, void 0, isSpace);
|
|
4553
|
-
}
|
|
4554
|
-
}
|
|
4555
|
-
catch (error) {
|
|
4556
|
-
if (error instanceof Error) {
|
|
4557
|
-
Logger.error(`Error extracting representation item\n\t${error.message}\n\tExpress ID: #${item.expressID}`);
|
|
4558
|
-
}
|
|
4559
|
-
else {
|
|
4560
|
-
Logger.error(`Error extracting representation item\n\tExpress ID: #${item.expressID}`);
|
|
4561
|
-
}
|
|
4562
|
-
}
|
|
4563
|
-
}
|
|
4564
|
-
const styledItemLocalID_ = this.materials.styledItemMap.get(item.localID);
|
|
4565
|
-
if (styledItemLocalID_ !== undefined) {
|
|
4566
|
-
const styledItem_ = this.model.getElementByLocalID(styledItemLocalID_);
|
|
4567
|
-
this.extractStyledItem(styledItem_);
|
|
4568
|
-
}
|
|
4569
|
-
else if (reusableStyleID !== void 0) {
|
|
4570
|
-
this.materials.addGeometryMapping(item.localID, reusableStyleID);
|
|
4571
|
-
}
|
|
4572
|
-
else if (styledItem instanceof IfcStyledItem) {
|
|
4573
|
-
// here we have the styled item, apply it to all geometry in this IfcProduct
|
|
4574
|
-
reusableStyleID = this.extractStyledItem(styledItem, item);
|
|
4575
|
-
}
|
|
4576
|
-
}
|
|
4577
|
-
}
|
|
4578
|
-
}
|
|
4579
|
-
else {
|
|
4580
|
-
for (const representation of representations.Representations) {
|
|
4581
|
-
if (representation instanceof IfcShapeRepresentation) {
|
|
4582
|
-
// this check is essential -
|
|
4583
|
-
// if RepresentationIdentifier !== Body or Facetation we must skip it
|
|
4584
|
-
if (representation.RepresentationIdentifier !== "Body" &&
|
|
4585
|
-
representation.RepresentationIdentifier !== "Facetation") {
|
|
4586
|
-
continue;
|
|
4587
|
-
}
|
|
4588
|
-
}
|
|
4589
|
-
for (const item of representation.Items) {
|
|
4590
|
-
if (item instanceof IfcMappedItem) {
|
|
4591
|
-
this.extractMappedItem(item, product, hasRelVoid, isSpace);
|
|
4592
|
-
}
|
|
4593
|
-
else {
|
|
4594
|
-
try {
|
|
4595
|
-
this.extractRepresentationItem(item, product.localID, hasRelVoid, isSpace);
|
|
4596
|
-
if (hasRelVoid) {
|
|
4597
|
-
this.applyRelVoidToRepresentation(item, relVoidsMeshVector, product.localID, relVoidLocalIDs, void 0, isSpace);
|
|
4598
|
-
}
|
|
4599
|
-
const styledItemLocalID_ = this.materials.styledItemMap.get(item.localID);
|
|
4600
|
-
if (styledItemLocalID_ !== void 0) {
|
|
4601
|
-
const styledItem_ = this.model.getElementByLocalID(styledItemLocalID_);
|
|
4602
|
-
this.extractStyledItem(styledItem_);
|
|
4603
|
-
}
|
|
4604
|
-
}
|
|
4605
|
-
catch (error) {
|
|
4606
|
-
if (error instanceof Error) {
|
|
4607
|
-
Logger.error(`Error extracting representation item\n\t${error.message}\n\t${error.stack}\n\tExpress ID: #${item.localID}`);
|
|
4608
|
-
}
|
|
4609
|
-
else {
|
|
4610
|
-
Logger.error(`Unknown error extracting representation item Express ID: #${item.localID}`);
|
|
4611
|
-
}
|
|
4612
|
-
}
|
|
4613
|
-
}
|
|
4614
|
-
}
|
|
4615
|
-
}
|
|
4616
|
-
}
|
|
4617
|
-
relVoidsMeshVector?.delete();
|
|
4618
|
-
}
|
|
4685
|
+
this.extractProductGeometry(product);
|
|
4619
4686
|
}
|
|
4620
4687
|
const relAggregates = this.model.types(IfcRelAggregates);
|
|
4621
4688
|
for (const relAggregate of relAggregates) {
|
|
@@ -4632,120 +4699,7 @@ export class IfcGeometryExtraction {
|
|
|
4632
4699
|
const relatedObjects = relAggregate.RelatedObjects;
|
|
4633
4700
|
for (const productRepresentation of relatedObjects) {
|
|
4634
4701
|
if (productRepresentation instanceof IfcProduct) {
|
|
4635
|
-
|
|
4636
|
-
productRepresentation instanceof IfcSpace ||
|
|
4637
|
-
productRepresentation instanceof IfcOpeningStandardCase;
|
|
4638
|
-
const product = productRepresentation;
|
|
4639
|
-
this.scene.clearParentStack();
|
|
4640
|
-
const objectPlacement = product.ObjectPlacement;
|
|
4641
|
-
if (objectPlacement !== null) {
|
|
4642
|
-
this.extractPlacement(objectPlacement);
|
|
4643
|
-
}
|
|
4644
|
-
const representations = product.Representation;
|
|
4645
|
-
if (representations !== null) {
|
|
4646
|
-
// extract styledItem material
|
|
4647
|
-
const styledItemID = this.extractMaterialStyle(product);
|
|
4648
|
-
const extractRelVoidsResult = masterRelVoids ?? this.extractRelVoids(product);
|
|
4649
|
-
let hasRelVoid = false;
|
|
4650
|
-
let relVoidsMeshVector;
|
|
4651
|
-
let relVoidLocalIDs;
|
|
4652
|
-
if (extractRelVoidsResult !== void 0) {
|
|
4653
|
-
[relVoidsMeshVector, relVoidLocalIDs] = extractRelVoidsResult;
|
|
4654
|
-
}
|
|
4655
|
-
if (relVoidsMeshVector !== void 0) {
|
|
4656
|
-
hasRelVoid = true;
|
|
4657
|
-
}
|
|
4658
|
-
if (styledItemID !== void 0) {
|
|
4659
|
-
// optimization: extract the first representation item and cache
|
|
4660
|
-
// the styleID to apply to the rest of the product geometry
|
|
4661
|
-
const styledItem = this.model.getElementByLocalID(styledItemID);
|
|
4662
|
-
let reusableStyleID;
|
|
4663
|
-
for (const representation of representations.Representations) {
|
|
4664
|
-
if (representation instanceof IfcShapeRepresentation) {
|
|
4665
|
-
// this check is essential -
|
|
4666
|
-
// if RepresentationIdentifier !== Body or Facetation we must skip it
|
|
4667
|
-
if (representation.RepresentationIdentifier !== "Body" &&
|
|
4668
|
-
representation.RepresentationIdentifier !== "Facetation") {
|
|
4669
|
-
continue;
|
|
4670
|
-
}
|
|
4671
|
-
}
|
|
4672
|
-
for (const item of representation.Items) {
|
|
4673
|
-
if (item instanceof IfcMappedItem) {
|
|
4674
|
-
this.extractMappedItem(item, product, hasRelVoid, isSpace);
|
|
4675
|
-
}
|
|
4676
|
-
else {
|
|
4677
|
-
try {
|
|
4678
|
-
this.extractRepresentationItem(item, product.localID, hasRelVoid, isSpace);
|
|
4679
|
-
if (hasRelVoid) {
|
|
4680
|
-
this.applyRelVoidToRepresentation(item, relVoidsMeshVector, product.localID, relVoidLocalIDs, void 0, isSpace);
|
|
4681
|
-
}
|
|
4682
|
-
}
|
|
4683
|
-
catch (error) {
|
|
4684
|
-
if (error instanceof Error) {
|
|
4685
|
-
Logger.error(`Error extracting rel aggregate representation item\n\t${error.message}\n\t${error.stack}\n\t Express ID: #${item.expressID}`);
|
|
4686
|
-
}
|
|
4687
|
-
else {
|
|
4688
|
-
Logger.error(`Unknown error extracting rel aggregate representation item Express ID: #${item.expressID}`);
|
|
4689
|
-
}
|
|
4690
|
-
}
|
|
4691
|
-
}
|
|
4692
|
-
const styledItemLocalID_ = this.materials.styledItemMap.get(item.localID);
|
|
4693
|
-
if (styledItemLocalID_ !== undefined) {
|
|
4694
|
-
const styledItem_ = this.model.getElementByLocalID(styledItemLocalID_);
|
|
4695
|
-
this.extractStyledItem(styledItem_);
|
|
4696
|
-
}
|
|
4697
|
-
else if (reusableStyleID !== void 0) {
|
|
4698
|
-
this.materials.addGeometryMapping(item.localID, reusableStyleID);
|
|
4699
|
-
}
|
|
4700
|
-
else if (styledItem instanceof IfcStyledItem) {
|
|
4701
|
-
// here we have the styled item, apply it to all geometry in this IfcProduct
|
|
4702
|
-
reusableStyleID = this.extractStyledItem(styledItem, item);
|
|
4703
|
-
}
|
|
4704
|
-
}
|
|
4705
|
-
}
|
|
4706
|
-
}
|
|
4707
|
-
else {
|
|
4708
|
-
for (const representation of representations.Representations) {
|
|
4709
|
-
if (representation instanceof IfcShapeRepresentation) {
|
|
4710
|
-
// this check is essential -
|
|
4711
|
-
// if RepresentationIdentifier !== Body or Facetation we must skip it
|
|
4712
|
-
if (representation.RepresentationIdentifier !== "Body" &&
|
|
4713
|
-
representation.RepresentationIdentifier !== "Facetation") {
|
|
4714
|
-
continue;
|
|
4715
|
-
}
|
|
4716
|
-
}
|
|
4717
|
-
for (const item of representation.Items) {
|
|
4718
|
-
if (item instanceof IfcMappedItem) {
|
|
4719
|
-
this.extractMappedItem(item, product, hasRelVoid, isSpace);
|
|
4720
|
-
}
|
|
4721
|
-
else {
|
|
4722
|
-
try {
|
|
4723
|
-
this.extractRepresentationItem(item, product.localID, hasRelVoid, isSpace);
|
|
4724
|
-
if (hasRelVoid) {
|
|
4725
|
-
this.applyRelVoidToRepresentation(item, relVoidsMeshVector, product.localID, relVoidLocalIDs, void 0, isSpace);
|
|
4726
|
-
}
|
|
4727
|
-
const styledItemLocalID_ = this.materials.styledItemMap.get(item.localID);
|
|
4728
|
-
if (styledItemLocalID_ !== void 0) {
|
|
4729
|
-
const styledItem_ = this.model.getElementByLocalID(styledItemLocalID_);
|
|
4730
|
-
this.extractStyledItem(styledItem_);
|
|
4731
|
-
}
|
|
4732
|
-
}
|
|
4733
|
-
catch (error) {
|
|
4734
|
-
if (error instanceof Error) {
|
|
4735
|
-
Logger.error(`Error extracting rel aggregate representation item\n\t${error.message}\n\t${error.stack}\n\t Express ID: #${item.expressID}`);
|
|
4736
|
-
}
|
|
4737
|
-
else {
|
|
4738
|
-
Logger.error(`Unknown error extracting rel aggregate representation item Express ID: #${item.expressID}`);
|
|
4739
|
-
}
|
|
4740
|
-
}
|
|
4741
|
-
}
|
|
4742
|
-
}
|
|
4743
|
-
}
|
|
4744
|
-
}
|
|
4745
|
-
if (masterRelVoids === void 0) {
|
|
4746
|
-
relVoidsMeshVector?.delete();
|
|
4747
|
-
}
|
|
4748
|
-
}
|
|
4702
|
+
this.extractProductGeometry(productRepresentation, masterRelVoids);
|
|
4749
4703
|
}
|
|
4750
4704
|
}
|
|
4751
4705
|
if (masterRelVoids !== void 0) {
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { ByteSource } from "../step/parsing/byte_source.js";
|
|
2
|
+
import { StepIndexColumns } from "../step/parsing/columnar_index.js";
|
|
3
|
+
import { StreamingIndexStats } from "../step/parsing/streaming_index_builder.js";
|
|
4
|
+
import { ParseResult, StepHeader } from "../step/parsing/step_parser.js";
|
|
5
|
+
import { StepExternalByteStore } from "../step/step_buffer_provider.js";
|
|
6
|
+
import EntityTypesIfc from "./ifc4_gen/entity_types_ifc.gen.js";
|
|
7
|
+
import IfcStepModel from "./ifc_step_model.js";
|
|
8
|
+
/**
|
|
9
|
+
* Options for a streamed IFC open. All optional; defaults suit a browser
|
|
10
|
+
* worker over an OPFS-backed store.
|
|
11
|
+
*/
|
|
12
|
+
export interface StreamedIfcOpenOptions {
|
|
13
|
+
/** Parse window size in bytes (default 1 MiB). */
|
|
14
|
+
pool?: number;
|
|
15
|
+
/** Windowed provider chunk size (default: provider's). */
|
|
16
|
+
chunkBytes?: number;
|
|
17
|
+
/** Windowed provider LRU cap in chunks (default: provider's). */
|
|
18
|
+
maxResidentChunks?: number;
|
|
19
|
+
/**
|
|
20
|
+
* Live per-record event `(localID, expressID, typeID)` — the M2 seam for
|
|
21
|
+
* incremental consumers (type index, roots, cross-refs) that run while
|
|
22
|
+
* the model is still parsing. Must be synchronous and cheap.
|
|
23
|
+
*/
|
|
24
|
+
onRecordIndexed?: (localID: number, expressID: number, typeID: EntityTypesIfc | undefined) => void;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The result of a streamed IFC open: the windowed-source model plus the
|
|
28
|
+
* artifacts a consumer needs around it.
|
|
29
|
+
*/
|
|
30
|
+
export interface StreamedIfcOpen {
|
|
31
|
+
/** The model over the windowed source (undefined if the parse failed). */
|
|
32
|
+
model: IfcStepModel | undefined;
|
|
33
|
+
/** The parse result. */
|
|
34
|
+
result: ParseResult;
|
|
35
|
+
/** The STEP header (available even on some failed parses). */
|
|
36
|
+
header: StepHeader;
|
|
37
|
+
/**
|
|
38
|
+
* The columnar index the model was built from. Hand to
|
|
39
|
+
* `serializeIndexSidecarFromColumns` (with a source hash) to produce the
|
|
40
|
+
* revisit sidecar — the columns ARE the sidecar payload (M7 identity).
|
|
41
|
+
*/
|
|
42
|
+
columns: StepIndexColumns<EntityTypesIfc>;
|
|
43
|
+
/** Window diagnostics from the streaming build. */
|
|
44
|
+
stats: StreamingIndexStats;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Open an IFC model from a streamed source with a **fixed-memory parse**
|
|
48
|
+
* (the release-facing Phase B API; composes M0/M1a/M7):
|
|
49
|
+
*
|
|
50
|
+
* - the index builds through a moving window over `source` — the source is
|
|
51
|
+
* never resident in the JS heap, and the index is columnar from birth
|
|
52
|
+
* (no per-record object phase);
|
|
53
|
+
* - the model reads source bytes on demand through a windowed LRU provider
|
|
54
|
+
* over `store` (`ensureResidentByLocalID`/`ByExpressID` page ranges in
|
|
55
|
+
* before synchronous reads — see `DemandResidencyPump` for the demand
|
|
56
|
+
* orchestration).
|
|
57
|
+
*
|
|
58
|
+
* The typical browser wiring: a worker streams the network body **through**
|
|
59
|
+
* to OPFS while feeding the same bytes to `source`; `store` reads back from
|
|
60
|
+
* OPFS. `source.byteLength` and `store.byteLength` must agree.
|
|
61
|
+
*
|
|
62
|
+
* @param source The sequential parse source.
|
|
63
|
+
* @param store The random-access store the model reads from afterwards.
|
|
64
|
+
* @param options See {@link StreamedIfcOpenOptions}.
|
|
65
|
+
* @return {StreamedIfcOpen} The model + header, columns, and diagnostics.
|
|
66
|
+
*/
|
|
67
|
+
export declare function openStreamedIfcModel(source: ByteSource, store: StepExternalByteStore, options?: StreamedIfcOpenOptions): StreamedIfcOpen;
|
|
68
|
+
//# sourceMappingURL=ifc_stream_open.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ifc_stream_open.d.ts","sourceRoot":"","sources":["../../../src/ifc/ifc_stream_open.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AACjE,OAAO,EAEL,mBAAmB,EACpB,MAAM,yCAAyC,CAAA;AAChD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AACrE,OAAO,EACL,qBAAqB,EAEtB,MAAM,8BAA8B,CAAA;AACrC,OAAO,cAAc,MAAM,iCAAiC,CAAA;AAC5D,OAAO,YAAY,MAAM,kBAAkB,CAAA;AAI3C;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IAErC,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAA;IAEb,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB,iEAAiE;IACjE,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAE1B;;;;OAIG;IACH,eAAe,CAAC,EACd,CAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,SAAS,KAAM,IAAI,CAAA;CACrF;AAGD;;;GAGG;AACH,MAAM,WAAW,eAAe;IAE9B,0EAA0E;IAC1E,KAAK,EAAE,YAAY,GAAG,SAAS,CAAA;IAE/B,wBAAwB;IACxB,MAAM,EAAE,WAAW,CAAA;IAEnB,8DAA8D;IAC9D,MAAM,EAAE,UAAU,CAAA;IAElB;;;;OAIG;IACH,OAAO,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAA;IAEzC,mDAAmD;IACnD,KAAK,EAAE,mBAAmB,CAAA;CAC3B;AAOD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAChC,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,qBAAqB,EAC5B,OAAO,CAAC,EAAE,sBAAsB,GAAI,eAAe,CA4BtD"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { buildColumnarIndexStreaming, } from "../step/parsing/streaming_index_builder.js";
|
|
2
|
+
import { ParseResult } from "../step/parsing/step_parser.js";
|
|
3
|
+
import { WindowedStepBufferProvider, } from "../step/step_buffer_provider.js";
|
|
4
|
+
import IfcStepModel from "./ifc_step_model.js";
|
|
5
|
+
import IfcStepParser from "./ifc_step_parser.js";
|
|
6
|
+
// eslint-disable-next-line no-magic-numbers
|
|
7
|
+
const DEFAULT_STREAM_POOL_BYTES = 1024 * 1024;
|
|
8
|
+
/**
|
|
9
|
+
* Open an IFC model from a streamed source with a **fixed-memory parse**
|
|
10
|
+
* (the release-facing Phase B API; composes M0/M1a/M7):
|
|
11
|
+
*
|
|
12
|
+
* - the index builds through a moving window over `source` — the source is
|
|
13
|
+
* never resident in the JS heap, and the index is columnar from birth
|
|
14
|
+
* (no per-record object phase);
|
|
15
|
+
* - the model reads source bytes on demand through a windowed LRU provider
|
|
16
|
+
* over `store` (`ensureResidentByLocalID`/`ByExpressID` page ranges in
|
|
17
|
+
* before synchronous reads — see `DemandResidencyPump` for the demand
|
|
18
|
+
* orchestration).
|
|
19
|
+
*
|
|
20
|
+
* The typical browser wiring: a worker streams the network body **through**
|
|
21
|
+
* to OPFS while feeding the same bytes to `source`; `store` reads back from
|
|
22
|
+
* OPFS. `source.byteLength` and `store.byteLength` must agree.
|
|
23
|
+
*
|
|
24
|
+
* @param source The sequential parse source.
|
|
25
|
+
* @param store The random-access store the model reads from afterwards.
|
|
26
|
+
* @param options See {@link StreamedIfcOpenOptions}.
|
|
27
|
+
* @return {StreamedIfcOpen} The model + header, columns, and diagnostics.
|
|
28
|
+
*/
|
|
29
|
+
export function openStreamedIfcModel(source, store, options) {
|
|
30
|
+
if (store.byteLength !== source.byteLength) {
|
|
31
|
+
throw new Error(`Streaming store byteLength ${store.byteLength} does not match ` +
|
|
32
|
+
`source byteLength ${source.byteLength}`);
|
|
33
|
+
}
|
|
34
|
+
const { header, columns, result, stats } = buildColumnarIndexStreaming(source, IfcStepParser.Instance, options?.pool ?? DEFAULT_STREAM_POOL_BYTES, options?.onRecordIndexed);
|
|
35
|
+
if (result !== ParseResult.COMPLETE) {
|
|
36
|
+
return { model: void 0, result, header, columns, stats };
|
|
37
|
+
}
|
|
38
|
+
const provider = new WindowedStepBufferProvider(store, options?.chunkBytes, options?.maxResidentChunks);
|
|
39
|
+
return {
|
|
40
|
+
model: new IfcStepModel(void 0, columns, provider),
|
|
41
|
+
result,
|
|
42
|
+
header,
|
|
43
|
+
columns,
|
|
44
|
+
stats,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ifc_stream_open.test.d.ts","sourceRoot":"","sources":["../../../src/ifc/ifc_stream_open.test.ts"],"names":[],"mappings":""}
|