@bldrs-ai/conway 1.375.1183 → 1.376.1184
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 +1 -1
- package/compiled/examples/cli-step-bundled.cjs +1 -1
- package/compiled/examples/validator-bundled.cjs +1 -1
- package/compiled/src/AP214E3_2010/ap214_product_structure_extraction.d.ts +89 -3
- package/compiled/src/AP214E3_2010/ap214_product_structure_extraction.d.ts.map +1 -1
- package/compiled/src/AP214E3_2010/ap214_product_structure_extraction.js +195 -15
- package/compiled/src/AP214E3_2010/ap214_product_structure_extraction.test.js +97 -4
- package/compiled/src/compat/web-ifc/ap214_properties.d.ts +16 -2
- package/compiled/src/compat/web-ifc/ap214_properties.d.ts.map +1 -1
- package/compiled/src/compat/web-ifc/ap214_properties.js +33 -2
- package/compiled/src/compat/web-ifc/ap214_properties.test.js +22 -0
- package/compiled/src/compat/web-ifc/properties.d.ts +2 -2
- package/compiled/src/compat/web-ifc/properties.d.ts.map +1 -1
- package/compiled/src/compat/web-ifc/properties.js +3 -2
- package/compiled/src/compat/web-ifc/properties_passthrough.d.ts +13 -1
- package/compiled/src/compat/web-ifc/properties_passthrough.d.ts.map +1 -1
- package/compiled/src/version/version.js +1 -1
- package/compiled/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -70042,7 +70042,7 @@ IfcStepParser.Instance = new IfcStepParser();
|
|
|
70042
70042
|
var ifc_step_parser_default = IfcStepParser;
|
|
70043
70043
|
|
|
70044
70044
|
// compiled/src/version/version.js
|
|
70045
|
-
var versionString = "Conway Web-Ifc Shim v1.
|
|
70045
|
+
var versionString = "Conway Web-Ifc Shim v1.376.1184";
|
|
70046
70046
|
|
|
70047
70047
|
// compiled/src/statistics/statistics.js
|
|
70048
70048
|
var Statistics = class {
|
|
@@ -86283,7 +86283,7 @@ var IfcSceneBuilder = class {
|
|
|
86283
86283
|
};
|
|
86284
86284
|
|
|
86285
86285
|
// compiled/src/version/version.js
|
|
86286
|
-
var versionString = "Conway Web-Ifc Shim v1.
|
|
86286
|
+
var versionString = "Conway Web-Ifc Shim v1.376.1184";
|
|
86287
86287
|
|
|
86288
86288
|
// compiled/src/statistics/statistics.js
|
|
86289
86289
|
var Statistics = class {
|
|
@@ -79734,7 +79734,7 @@ var ExtractResult;
|
|
|
79734
79734
|
})(ExtractResult || (ExtractResult = {}));
|
|
79735
79735
|
|
|
79736
79736
|
// compiled/src/version/version.js
|
|
79737
|
-
var versionString = "Conway Web-Ifc Shim v1.
|
|
79737
|
+
var versionString = "Conway Web-Ifc Shim v1.376.1184";
|
|
79738
79738
|
|
|
79739
79739
|
// compiled/src/statistics/statistics.js
|
|
79740
79740
|
var Statistics = class {
|
|
@@ -70040,7 +70040,7 @@ IfcStepParser.Instance = new IfcStepParser();
|
|
|
70040
70040
|
var ifc_step_parser_default = IfcStepParser;
|
|
70041
70041
|
|
|
70042
70042
|
// compiled/src/version/version.js
|
|
70043
|
-
var versionString = "Conway Web-Ifc Shim v1.
|
|
70043
|
+
var versionString = "Conway Web-Ifc Shim v1.376.1184";
|
|
70044
70044
|
|
|
70045
70045
|
// compiled/src/statistics/statistics.js
|
|
70046
70046
|
var Statistics = class {
|
|
@@ -23,7 +23,10 @@ export interface ProductStructureNode {
|
|
|
23
23
|
* `product_definition` express id.
|
|
24
24
|
*/
|
|
25
25
|
expressID: number;
|
|
26
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* Readable node kind: `'product'` for roots, `'product_occurrence'` for NAUO
|
|
28
|
+
* nodes, `'solid'` for ephemeral sub-product solid nodes.
|
|
29
|
+
*/
|
|
27
30
|
type: string;
|
|
28
31
|
/** Display label: `product.name`, falling back to the NAUO name / reference designator. */
|
|
29
32
|
name: string;
|
|
@@ -43,9 +46,54 @@ export interface ProductStructureNode {
|
|
|
43
46
|
* scene geometry for NavTree-click ⇄ viewport-pick round-tripping.
|
|
44
47
|
*/
|
|
45
48
|
shapeRepresentationIds: number[];
|
|
46
|
-
/** Child occurrence nodes. */
|
|
49
|
+
/** Child occurrence nodes (and, when opted in, ephemeral solid nodes). */
|
|
47
50
|
children: ProductStructureNode[];
|
|
51
|
+
/**
|
|
52
|
+
* True for ephemeral (non-product) nodes — pickable geometry that carries
|
|
53
|
+
* identity in the file but has no product semantics, e.g. one named solid of
|
|
54
|
+
* a multibody part. Consumers should render these lighter-weight,
|
|
55
|
+
* selectable-but-not-product. See `design/new/step-nonproduct-semantics.md`.
|
|
56
|
+
*/
|
|
57
|
+
ephemeral?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Number of this node's solids suppressed by the ephemeral-layer limits
|
|
60
|
+
* (unnamed-soup suppression or the per-product cap), so a consumer can
|
|
61
|
+
* render an "N more…" affordance instead of silently truncating.
|
|
62
|
+
*/
|
|
63
|
+
droppedSolids?: number;
|
|
48
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Options for {@link AP214ProductStructureExtraction.extractProductStructure}.
|
|
67
|
+
*/
|
|
68
|
+
export interface ProductStructureOptions {
|
|
69
|
+
/**
|
|
70
|
+
* Surface an ephemeral layer of solid-level nodes beneath each multibody
|
|
71
|
+
* product (default false). A product only gets solid children when its shape
|
|
72
|
+
* representation holds at least two solids — a single-solid product already
|
|
73
|
+
* maps 1:1 onto its node — and an all-unnamed set larger than
|
|
74
|
+
* {@link maxUnnamedSolidsPerProduct} is suppressed as meaningless "solid
|
|
75
|
+
* soup" (the DSA2 case: 28k unnamed single-face shells under one product).
|
|
76
|
+
*/
|
|
77
|
+
includeSolids?: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Hard cap on emitted solid children per product occurrence
|
|
80
|
+
* (default {@link DEFAULT_MAX_SOLIDS_PER_PRODUCT}); overflow is reported via
|
|
81
|
+
* {@link ProductStructureNode.droppedSolids}.
|
|
82
|
+
*/
|
|
83
|
+
maxSolidsPerProduct?: number;
|
|
84
|
+
/**
|
|
85
|
+
* When a product's solids are *all* unnamed and outnumber this (default
|
|
86
|
+
* {@link DEFAULT_MAX_UNNAMED_SOLIDS_PER_PRODUCT}), emit none of them:
|
|
87
|
+
* large anonymous solid dumps (ECAD merged-component products, tessellated
|
|
88
|
+
* surface soup) carry no navigable semantics. Named solids are never
|
|
89
|
+
* suppressed by this limit.
|
|
90
|
+
*/
|
|
91
|
+
maxUnnamedSolidsPerProduct?: number;
|
|
92
|
+
}
|
|
93
|
+
/** Default hard cap on emitted solid children per product occurrence. */
|
|
94
|
+
export declare const DEFAULT_MAX_SOLIDS_PER_PRODUCT = 256;
|
|
95
|
+
/** Default suppression threshold for a product whose solids are all unnamed. */
|
|
96
|
+
export declare const DEFAULT_MAX_UNNAMED_SOLIDS_PER_PRODUCT = 32;
|
|
49
97
|
/**
|
|
50
98
|
* Extracts the STEP product / assembly structure from a populated
|
|
51
99
|
* {@link AP214StepModel} into a nested, named, occurrence-keyed tree.
|
|
@@ -63,6 +111,11 @@ export declare class AP214ProductStructureExtraction {
|
|
|
63
111
|
private readonly childProductDefIds_;
|
|
64
112
|
private readonly productDefById_;
|
|
65
113
|
private readonly shapeRepsByProductDef_;
|
|
114
|
+
private readonly solidsByProductDef_;
|
|
115
|
+
private readonly solidIdsByProductDef_;
|
|
116
|
+
private includeSolids_;
|
|
117
|
+
private maxSolidsPerProduct_;
|
|
118
|
+
private maxUnnamedSolidsPerProduct_;
|
|
66
119
|
/**
|
|
67
120
|
* @param model The populated AP214/AP242 step model to walk.
|
|
68
121
|
* @param productShapeMap Optional product↔shape map populated during geometry
|
|
@@ -75,11 +128,14 @@ export declare class AP214ProductStructureExtraction {
|
|
|
75
128
|
/**
|
|
76
129
|
* Build the product-structure tree.
|
|
77
130
|
*
|
|
131
|
+
* @param options Optional {@link ProductStructureOptions}; pass
|
|
132
|
+
* `{ includeSolids: true }` to add the ephemeral solid layer beneath
|
|
133
|
+
* multibody products.
|
|
78
134
|
* @return {ProductStructureNode[]} The roots of the assembly forest. A
|
|
79
135
|
* single-part file yields one root; a multi-level assembly (e.g. `as1`) yields
|
|
80
136
|
* one root whose descendants are the NAUO occurrences.
|
|
81
137
|
*/
|
|
82
|
-
extractProductStructure(): ProductStructureNode[];
|
|
138
|
+
extractProductStructure(options?: ProductStructureOptions): ProductStructureNode[];
|
|
83
139
|
/**
|
|
84
140
|
* Index every `product_definition` by express id for O(1) lookup during the
|
|
85
141
|
* recursive walk.
|
|
@@ -98,6 +154,26 @@ export declare class AP214ProductStructureExtraction {
|
|
|
98
154
|
* geometry-extraction product↔shape map.
|
|
99
155
|
*/
|
|
100
156
|
private indexShapeRepresentations;
|
|
157
|
+
/**
|
|
158
|
+
* Index the solid-level bodies of each product definition for the ephemeral
|
|
159
|
+
* layer: walk each `shape_definition_representation`'s representation, plus
|
|
160
|
+
* any representation reachable over a *plain* `shape_representation_relationship`
|
|
161
|
+
* (SolidWorks binds a part's multibody `advanced_brep_shape_representation`
|
|
162
|
+
* to its SDR-bound representation that way — the NEMA motor pattern), and
|
|
163
|
+
* collect the solid items. Transformation-bearing relationship variants are
|
|
164
|
+
* assembly placements (parent rep ↔ child rep), so following them would leak
|
|
165
|
+
* every child part's solids into its parent assembly; they are skipped.
|
|
166
|
+
*/
|
|
167
|
+
private indexSolids;
|
|
168
|
+
/**
|
|
169
|
+
* Record a solid for a product definition, de-duplicating by the solid's
|
|
170
|
+
* express id (a body can be reachable both directly and over a
|
|
171
|
+
* representation relationship).
|
|
172
|
+
*
|
|
173
|
+
* @param productDefId The owning product definition express id.
|
|
174
|
+
* @param solid The solid to record.
|
|
175
|
+
*/
|
|
176
|
+
private addSolid;
|
|
101
177
|
/**
|
|
102
178
|
* Record a shape-representation id for a product definition, de-duplicating.
|
|
103
179
|
*
|
|
@@ -118,6 +194,16 @@ export declare class AP214ProductStructureExtraction {
|
|
|
118
194
|
* @return {ProductStructureNode} The built node, with children.
|
|
119
195
|
*/
|
|
120
196
|
private buildNode;
|
|
197
|
+
/**
|
|
198
|
+
* Append this node's ephemeral solid children, applying the layer's
|
|
199
|
+
* heuristics (see {@link ProductStructureOptions}): nothing for a
|
|
200
|
+
* single-solid product, full suppression for oversized all-unnamed sets,
|
|
201
|
+
* and the hard per-product cap — suppressed/overflow counts are surfaced
|
|
202
|
+
* via {@link ProductStructureNode.droppedSolids}.
|
|
203
|
+
*
|
|
204
|
+
* @param node The product/occurrence node to attach solid children to.
|
|
205
|
+
*/
|
|
206
|
+
private appendSolidChildren;
|
|
121
207
|
/**
|
|
122
208
|
* Resolve a node label, preferring the product name, then the occurrence's
|
|
123
209
|
* own name / reference designator.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ap214_product_structure_extraction.d.ts","sourceRoot":"","sources":["../../../src/AP214E3_2010/ap214_product_structure_extraction.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,MAAM,oBAAoB,CAAA;AAC/C,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;
|
|
1
|
+
{"version":3,"file":"ap214_product_structure_extraction.d.ts","sourceRoot":"","sources":["../../../src/AP214E3_2010/ap214_product_structure_extraction.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,MAAM,oBAAoB,CAAA;AAC/C,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAkBhE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,oBAAoB;IAEnC;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAA;IAEjB;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAA;IAEZ,2FAA2F;IAC3F,IAAI,EAAE,MAAM,CAAA;IAEZ,2EAA2E;IAC3E,0BAA0B,EAAE,MAAM,CAAA;IAElC,mEAAmE;IACnE,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAE5B;;;;OAIG;IACH,cAAc,EAAE,MAAM,EAAE,CAAA;IAExB;;;;OAIG;IACH,sBAAsB,EAAE,MAAM,EAAE,CAAA;IAEhC,0EAA0E;IAC1E,QAAQ,EAAE,oBAAoB,EAAE,CAAA;IAEhC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IAEnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IAEtC;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IAEvB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAE5B;;;;;;OAMG;IACH,0BAA0B,CAAC,EAAE,MAAM,CAAA;CACpC;AAED,yEAAyE;AACzE,eAAO,MAAM,8BAA8B,MAAM,CAAA;AAEjD,gFAAgF;AAChF,eAAO,MAAM,sCAAsC,KAAK,CAAA;AAmDxD;;;;;;;;;GASG;AACH,qBAAa,+BAA+B;IAqBtC,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC;IApBrC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAsD;IACrF,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAoB;IACxD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAwC;IACxE,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAA8B;IACrE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAoC;IACxE,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAiC;IACvE,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,oBAAoB,CAAiC;IAC7D,OAAO,CAAC,2BAA2B,CAAyC;IAE5E;;;;;;;OAOG;gBAEkB,KAAK,EAAE,cAAc,EACrB,eAAe,CAAC,EAAE,oBAAoB,YAAA;IAG3D;;;;;;;;;OASG;IACI,uBAAuB,CAAE,OAAO,CAAC,EAAE,uBAAuB,GAAI,oBAAoB,EAAE;IAuC3F;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAa/B;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IA0B3B;;;;;OAKG;IACH,OAAO,CAAC,yBAAyB;IAiCjC;;;;;;;;;OASG;IACH,OAAO,CAAC,WAAW;IAuEnB;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ;IAoBhB;;;;;OAKG;IACH,OAAO,CAAC,sBAAsB;IAc9B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,SAAS;IA2DjB;;;;;;;;OAQG;IACH,OAAO,CAAC,mBAAmB;IA2C3B;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY;IA0BpB;;;;;;;;;OASG;IACH,MAAM,CAAC,0BAA0B,CAC7B,UAAU,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,GAAI,MAAM,GAAG,SAAS;CAgBzE"}
|
|
@@ -1,10 +1,45 @@
|
|
|
1
|
+
import { face_based_surface_model } from "./AP214E3_2010_gen/face_based_surface_model.gen.js";
|
|
2
|
+
import { manifold_solid_brep } from "./AP214E3_2010_gen/manifold_solid_brep.gen.js";
|
|
1
3
|
import { next_assembly_usage_occurrence } from "./AP214E3_2010_gen/next_assembly_usage_occurrence.gen.js";
|
|
2
4
|
import { product_definition } from "./AP214E3_2010_gen/product_definition.gen.js";
|
|
3
5
|
import { product_definition_shape } from "./AP214E3_2010_gen/product_definition_shape.gen.js";
|
|
6
|
+
import { representation_relationship_with_transformation, } from "./AP214E3_2010_gen/representation_relationship_with_transformation.gen.js";
|
|
4
7
|
import { shape_definition_representation } from "./AP214E3_2010_gen/shape_definition_representation.gen.js";
|
|
8
|
+
import { shape_representation_relationship, } from "./AP214E3_2010_gen/shape_representation_relationship.gen.js";
|
|
9
|
+
import { shell_based_surface_model } from "./AP214E3_2010_gen/shell_based_surface_model.gen.js";
|
|
10
|
+
/** Default hard cap on emitted solid children per product occurrence. */
|
|
11
|
+
export const DEFAULT_MAX_SOLIDS_PER_PRODUCT = 256;
|
|
12
|
+
/** Default suppression threshold for a product whose solids are all unnamed. */
|
|
13
|
+
export const DEFAULT_MAX_UNNAMED_SOLIDS_PER_PRODUCT = 32;
|
|
14
|
+
/** Minimum solids in a product before the ephemeral layer adds anything. */
|
|
15
|
+
const MIN_SOLIDS_FOR_EPHEMERAL = 2;
|
|
5
16
|
/** Readable node-kind strings for {@link ProductStructureNode.type}. */
|
|
6
17
|
const ROOT_NODE_TYPE = "product";
|
|
7
18
|
const OCCURRENCE_NODE_TYPE = "product_occurrence";
|
|
19
|
+
const SOLID_NODE_TYPE = "solid";
|
|
20
|
+
/**
|
|
21
|
+
* Whether a representation-item name carries meaning (non-empty and not an
|
|
22
|
+
* exporter placeholder).
|
|
23
|
+
*
|
|
24
|
+
* @param name The item name.
|
|
25
|
+
* @return {boolean} True when the name is worth displaying.
|
|
26
|
+
*/
|
|
27
|
+
function isMeaningfulName(name) {
|
|
28
|
+
return name.length > 0 && name !== "NONE" && name !== "UNKNOWN";
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Whether a representation item is a solid-level body that can anchor an
|
|
32
|
+
* ephemeral node ({@link manifold_solid_brep} covers its `brep_with_voids` /
|
|
33
|
+
* `faceted_brep` subtypes).
|
|
34
|
+
*
|
|
35
|
+
* @param item The representation item.
|
|
36
|
+
* @return {boolean} True for solid-level items.
|
|
37
|
+
*/
|
|
38
|
+
function isSolidItem(item) {
|
|
39
|
+
return item instanceof manifold_solid_brep ||
|
|
40
|
+
item instanceof shell_based_surface_model ||
|
|
41
|
+
item instanceof face_based_surface_model;
|
|
42
|
+
}
|
|
8
43
|
/**
|
|
9
44
|
* Extracts the STEP product / assembly structure from a populated
|
|
10
45
|
* {@link AP214StepModel} into a nested, named, occurrence-keyed tree.
|
|
@@ -31,18 +66,34 @@ export class AP214ProductStructureExtraction {
|
|
|
31
66
|
this.childProductDefIds_ = new Set();
|
|
32
67
|
this.productDefById_ = new Map();
|
|
33
68
|
this.shapeRepsByProductDef_ = new Map();
|
|
69
|
+
this.solidsByProductDef_ = new Map();
|
|
70
|
+
this.solidIdsByProductDef_ = new Map();
|
|
71
|
+
this.includeSolids_ = false;
|
|
72
|
+
this.maxSolidsPerProduct_ = DEFAULT_MAX_SOLIDS_PER_PRODUCT;
|
|
73
|
+
this.maxUnnamedSolidsPerProduct_ = DEFAULT_MAX_UNNAMED_SOLIDS_PER_PRODUCT;
|
|
34
74
|
}
|
|
35
75
|
/**
|
|
36
76
|
* Build the product-structure tree.
|
|
37
77
|
*
|
|
78
|
+
* @param options Optional {@link ProductStructureOptions}; pass
|
|
79
|
+
* `{ includeSolids: true }` to add the ephemeral solid layer beneath
|
|
80
|
+
* multibody products.
|
|
38
81
|
* @return {ProductStructureNode[]} The roots of the assembly forest. A
|
|
39
82
|
* single-part file yields one root; a multi-level assembly (e.g. `as1`) yields
|
|
40
83
|
* one root whose descendants are the NAUO occurrences.
|
|
41
84
|
*/
|
|
42
|
-
extractProductStructure() {
|
|
85
|
+
extractProductStructure(options) {
|
|
86
|
+
this.includeSolids_ = options?.includeSolids ?? false;
|
|
87
|
+
this.maxSolidsPerProduct_ =
|
|
88
|
+
options?.maxSolidsPerProduct ?? DEFAULT_MAX_SOLIDS_PER_PRODUCT;
|
|
89
|
+
this.maxUnnamedSolidsPerProduct_ =
|
|
90
|
+
options?.maxUnnamedSolidsPerProduct ?? DEFAULT_MAX_UNNAMED_SOLIDS_PER_PRODUCT;
|
|
43
91
|
this.indexProductDefinitions();
|
|
44
92
|
this.indexAssemblyUsages();
|
|
45
93
|
this.indexShapeRepresentations();
|
|
94
|
+
if (this.includeSolids_) {
|
|
95
|
+
this.indexSolids();
|
|
96
|
+
}
|
|
46
97
|
const roots = [];
|
|
47
98
|
for (const [productDefId, productDef] of this.productDefById_) {
|
|
48
99
|
if (this.childProductDefIds_.has(productDefId)) {
|
|
@@ -124,6 +175,90 @@ export class AP214ProductStructureExtraction {
|
|
|
124
175
|
}
|
|
125
176
|
}
|
|
126
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* Index the solid-level bodies of each product definition for the ephemeral
|
|
180
|
+
* layer: walk each `shape_definition_representation`'s representation, plus
|
|
181
|
+
* any representation reachable over a *plain* `shape_representation_relationship`
|
|
182
|
+
* (SolidWorks binds a part's multibody `advanced_brep_shape_representation`
|
|
183
|
+
* to its SDR-bound representation that way — the NEMA motor pattern), and
|
|
184
|
+
* collect the solid items. Transformation-bearing relationship variants are
|
|
185
|
+
* assembly placements (parent rep ↔ child rep), so following them would leak
|
|
186
|
+
* every child part's solids into its parent assembly; they are skipped.
|
|
187
|
+
*/
|
|
188
|
+
indexSolids() {
|
|
189
|
+
const relatedRepsByRepId = new Map();
|
|
190
|
+
const relate = (fromId, to) => {
|
|
191
|
+
if (fromId === void 0) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
let related = relatedRepsByRepId.get(fromId);
|
|
195
|
+
if (related === void 0) {
|
|
196
|
+
related = [];
|
|
197
|
+
relatedRepsByRepId.set(fromId, related);
|
|
198
|
+
}
|
|
199
|
+
related.push(to);
|
|
200
|
+
};
|
|
201
|
+
for (const element of this.model.types(shape_representation_relationship)) {
|
|
202
|
+
const srr = element;
|
|
203
|
+
if (srr.findVariant(representation_relationship_with_transformation) !== void 0) {
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
const rep1 = srr.rep_1;
|
|
207
|
+
const rep2 = srr.rep_2;
|
|
208
|
+
if (rep1 === void 0 || rep2 === void 0) {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
// The geometry-bearing representation may be on either side; bridge both
|
|
212
|
+
// ways and let solid-item filtering pick out the bodies.
|
|
213
|
+
relate(rep1.expressID, rep2);
|
|
214
|
+
relate(rep2.expressID, rep1);
|
|
215
|
+
}
|
|
216
|
+
for (const element of this.model.types(shape_definition_representation)) {
|
|
217
|
+
const sdr = element;
|
|
218
|
+
const productDefId = AP214ProductStructureExtraction.resolveProductDefinitionId(sdr.definition);
|
|
219
|
+
const usedRep = sdr.used_representation;
|
|
220
|
+
if (productDefId === void 0 || usedRep === void 0) {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
const reps = [usedRep, ...(relatedRepsByRepId.get(usedRep.expressID ?? -1) ?? [])];
|
|
224
|
+
for (const rep of reps) {
|
|
225
|
+
for (const item of rep.items) {
|
|
226
|
+
if (!isSolidItem(item) || item.expressID === void 0) {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
this.addSolid(productDefId, {
|
|
230
|
+
expressID: item.expressID,
|
|
231
|
+
name: item.name,
|
|
232
|
+
representationId: rep.expressID,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Record a solid for a product definition, de-duplicating by the solid's
|
|
240
|
+
* express id (a body can be reachable both directly and over a
|
|
241
|
+
* representation relationship).
|
|
242
|
+
*
|
|
243
|
+
* @param productDefId The owning product definition express id.
|
|
244
|
+
* @param solid The solid to record.
|
|
245
|
+
*/
|
|
246
|
+
addSolid(productDefId, solid) {
|
|
247
|
+
let solids = this.solidsByProductDef_.get(productDefId);
|
|
248
|
+
let solidIds = this.solidIdsByProductDef_.get(productDefId);
|
|
249
|
+
if (solids === void 0 || solidIds === void 0) {
|
|
250
|
+
solids = [];
|
|
251
|
+
solidIds = new Set();
|
|
252
|
+
this.solidsByProductDef_.set(productDefId, solids);
|
|
253
|
+
this.solidIdsByProductDef_.set(productDefId, solidIds);
|
|
254
|
+
}
|
|
255
|
+
// Set-backed dedup: a linear scan here is quadratic over a product's
|
|
256
|
+
// solids, and a single product can hold tens of thousands (DSA2).
|
|
257
|
+
if (!solidIds.has(solid.expressID)) {
|
|
258
|
+
solidIds.add(solid.expressID);
|
|
259
|
+
solids.push(solid);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
127
262
|
/**
|
|
128
263
|
* Record a shape-representation id for a product definition, de-duplicating.
|
|
129
264
|
*
|
|
@@ -169,24 +304,69 @@ export class AP214ProductStructureExtraction {
|
|
|
169
304
|
children: [],
|
|
170
305
|
};
|
|
171
306
|
const childUsages = this.nauosByParent_.get(productDefId);
|
|
172
|
-
if (childUsages
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
307
|
+
if (childUsages !== void 0 && !onPath.has(productDefId)) {
|
|
308
|
+
onPath.add(productDefId);
|
|
309
|
+
for (const childUsage of childUsages) {
|
|
310
|
+
const childDefId = childUsage.related_product_definition?.expressID;
|
|
311
|
+
if (childDefId === void 0) {
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
const childDef = this.productDefById_.get(childDefId);
|
|
315
|
+
if (childDef === void 0) {
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
node.children.push(this.buildNode(childDef, childUsage, occurrencePath, onPath));
|
|
184
319
|
}
|
|
185
|
-
|
|
320
|
+
onPath.delete(productDefId);
|
|
321
|
+
}
|
|
322
|
+
// Ephemeral solids come after the occurrence children: they are the
|
|
323
|
+
// lighter-weight entries of the two.
|
|
324
|
+
if (this.includeSolids_) {
|
|
325
|
+
this.appendSolidChildren(node);
|
|
186
326
|
}
|
|
187
|
-
onPath.delete(productDefId);
|
|
188
327
|
return node;
|
|
189
328
|
}
|
|
329
|
+
/**
|
|
330
|
+
* Append this node's ephemeral solid children, applying the layer's
|
|
331
|
+
* heuristics (see {@link ProductStructureOptions}): nothing for a
|
|
332
|
+
* single-solid product, full suppression for oversized all-unnamed sets,
|
|
333
|
+
* and the hard per-product cap — suppressed/overflow counts are surfaced
|
|
334
|
+
* via {@link ProductStructureNode.droppedSolids}.
|
|
335
|
+
*
|
|
336
|
+
* @param node The product/occurrence node to attach solid children to.
|
|
337
|
+
*/
|
|
338
|
+
appendSolidChildren(node) {
|
|
339
|
+
const solids = this.solidsByProductDef_.get(node.productDefinitionExpressID);
|
|
340
|
+
if (solids === void 0 || solids.length < MIN_SOLIDS_FOR_EPHEMERAL) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
const hasNamedSolid = solids.some((solid) => isMeaningfulName(solid.name));
|
|
344
|
+
if (!hasNamedSolid && solids.length > this.maxUnnamedSolidsPerProduct_) {
|
|
345
|
+
node.droppedSolids = solids.length;
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
let emitted = solids;
|
|
349
|
+
if (solids.length > this.maxSolidsPerProduct_) {
|
|
350
|
+
emitted = solids.slice(0, this.maxSolidsPerProduct_);
|
|
351
|
+
node.droppedSolids = solids.length - emitted.length;
|
|
352
|
+
}
|
|
353
|
+
for (let index = 0; index < emitted.length; ++index) {
|
|
354
|
+
const solid = emitted[index];
|
|
355
|
+
node.children.push({
|
|
356
|
+
expressID: solid.expressID,
|
|
357
|
+
type: SOLID_NODE_TYPE,
|
|
358
|
+
name: isMeaningfulName(solid.name) ?
|
|
359
|
+
solid.name : `Solid ${index + 1} of ${solids.length}`,
|
|
360
|
+
productDefinitionExpressID: node.productDefinitionExpressID,
|
|
361
|
+
// A solid is not an occurrence: the path stays NAUO-only, and the
|
|
362
|
+
// selection identity is the (occurrencePath, expressID) pair.
|
|
363
|
+
occurrencePath: [...node.occurrencePath],
|
|
364
|
+
shapeRepresentationIds: solid.representationId !== void 0 ? [solid.representationId] : [],
|
|
365
|
+
children: [],
|
|
366
|
+
ephemeral: true,
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
}
|
|
190
370
|
/**
|
|
191
371
|
* Resolve a node label, preferring the product name, then the occurrence's
|
|
192
372
|
* own name / reference designator.
|
|
@@ -6,19 +6,29 @@ import { ParseResult } from "../step/parsing/step_parser.js";
|
|
|
6
6
|
import { AP214ProductStructureExtraction, } from "./ap214_product_structure_extraction.js";
|
|
7
7
|
const parser = AP214StepParser.Instance;
|
|
8
8
|
/**
|
|
9
|
-
* Parse
|
|
9
|
+
* Parse a hermetic fixture and extract its product structure.
|
|
10
10
|
*
|
|
11
|
+
* @param path The fixture path.
|
|
12
|
+
* @param options Optional extraction options (e.g. the ephemeral solid layer).
|
|
11
13
|
* @return {ProductStructureNode[]} The extracted assembly forest.
|
|
12
14
|
*/
|
|
13
|
-
function
|
|
14
|
-
const buffer = fs.readFileSync(
|
|
15
|
+
function extractStructure(path, options) {
|
|
16
|
+
const buffer = fs.readFileSync(path);
|
|
15
17
|
const bufferInput = new ParsingBuffer(buffer);
|
|
16
18
|
const headerResult = parser.parseHeader(bufferInput)[1];
|
|
17
19
|
expect(headerResult).toBe(ParseResult.COMPLETE);
|
|
18
20
|
const [result, model] = parser.parseDataToModel(bufferInput);
|
|
19
21
|
expect(model).not.toBe(void 0);
|
|
20
22
|
expect(result === ParseResult.COMPLETE || result === ParseResult.INCOMPLETE).toBe(true);
|
|
21
|
-
return new AP214ProductStructureExtraction(model).extractProductStructure();
|
|
23
|
+
return new AP214ProductStructureExtraction(model).extractProductStructure(options);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Parse the hermetic as1 assembly fixture and extract its product structure.
|
|
27
|
+
*
|
|
28
|
+
* @return {ProductStructureNode[]} The extracted assembly forest.
|
|
29
|
+
*/
|
|
30
|
+
function extractAs1Structure() {
|
|
31
|
+
return extractStructure("data/as1-assembly.step");
|
|
22
32
|
}
|
|
23
33
|
/**
|
|
24
34
|
* Find a direct child of a node by display name.
|
|
@@ -105,3 +115,86 @@ describe("AP214ProductStructureExtraction", () => {
|
|
|
105
115
|
expect(root.children.length).toBe(AS1_ROOT_CHILD_COUNT);
|
|
106
116
|
});
|
|
107
117
|
});
|
|
118
|
+
const MULTIBODY_FIXTURE = "data/ap214-multibody-part.step";
|
|
119
|
+
const WIDGET_SOLID_COUNT = 3;
|
|
120
|
+
const SOUP_SOLID_COUNT = 3;
|
|
121
|
+
/**
|
|
122
|
+
* Collect the ephemeral solid children of a node.
|
|
123
|
+
*
|
|
124
|
+
* @param node The parent node.
|
|
125
|
+
* @return {ProductStructureNode[]} The `type: 'solid'` children.
|
|
126
|
+
*/
|
|
127
|
+
function solidChildren(node) {
|
|
128
|
+
return node.children.filter((candidate) => candidate.type === "solid");
|
|
129
|
+
}
|
|
130
|
+
describe("AP214ProductStructureExtraction ephemeral solid layer", () => {
|
|
131
|
+
test("is off by default: no solid nodes anywhere", () => {
|
|
132
|
+
const root = extractStructure(MULTIBODY_FIXTURE)[0];
|
|
133
|
+
const stack = [root];
|
|
134
|
+
while (stack.length > 0) {
|
|
135
|
+
const node = stack.pop();
|
|
136
|
+
expect(node.type).not.toBe("solid");
|
|
137
|
+
expect(node.ephemeral).toBe(void 0);
|
|
138
|
+
stack.push(...node.children);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
test("surfaces named multibody solids beneath each occurrence", () => {
|
|
142
|
+
const root = extractStructure(MULTIBODY_FIXTURE, { includeSolids: true })[0];
|
|
143
|
+
const widgets = root.children.filter((node) => node.name === "widget");
|
|
144
|
+
expect(widgets.length).toBe(2);
|
|
145
|
+
for (const widget of widgets) {
|
|
146
|
+
const solids = solidChildren(widget);
|
|
147
|
+
expect(solids.length).toBe(WIDGET_SOLID_COUNT);
|
|
148
|
+
// Named bodies keep their file names; the unnamed one gets a
|
|
149
|
+
// positional fallback.
|
|
150
|
+
expect(solids.map((solid) => solid.name))
|
|
151
|
+
.toEqual(["Body1", "Body2", "Solid 3 of 3"]);
|
|
152
|
+
for (const solid of solids) {
|
|
153
|
+
expect(solid.ephemeral).toBe(true);
|
|
154
|
+
expect(solid.children.length).toBe(0);
|
|
155
|
+
expect(solid.productDefinitionExpressID)
|
|
156
|
+
.toBe(widget.productDefinitionExpressID);
|
|
157
|
+
// A solid is not an occurrence: it inherits the parent's NAUO-only
|
|
158
|
+
// path, and (path, expressID) is the selection identity.
|
|
159
|
+
expect(solid.occurrencePath).toEqual(widget.occurrencePath);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// The same part type under two occurrences repeats the same solid ids —
|
|
163
|
+
// the occurrence path is what tells the instances apart.
|
|
164
|
+
expect(solidChildren(widgets[0]).map((solid) => solid.expressID))
|
|
165
|
+
.toEqual(solidChildren(widgets[1]).map((solid) => solid.expressID));
|
|
166
|
+
expect(widgets[0].occurrencePath).not.toEqual(widgets[1].occurrencePath);
|
|
167
|
+
});
|
|
168
|
+
test("leaves single-solid products and the assembly root alone", () => {
|
|
169
|
+
const root = extractStructure(MULTIBODY_FIXTURE, { includeSolids: true })[0];
|
|
170
|
+
// The widget solids live in a representation bridged to the assembly's
|
|
171
|
+
// representation by a transformation-bearing relationship (an assembly
|
|
172
|
+
// placement); following it would leak child solids into the parent.
|
|
173
|
+
expect(solidChildren(root).length).toBe(0);
|
|
174
|
+
const pin = root.children.find((node) => node.name === "pin");
|
|
175
|
+
// One solid maps 1:1 onto the product node itself; a child adds nothing.
|
|
176
|
+
expect(solidChildren(pin).length).toBe(0);
|
|
177
|
+
expect(pin.droppedSolids).toBe(void 0);
|
|
178
|
+
});
|
|
179
|
+
test("suppresses oversized all-unnamed solid sets and reports the drop", () => {
|
|
180
|
+
const root = extractStructure(MULTIBODY_FIXTURE, { includeSolids: true, maxUnnamedSolidsPerProduct: 2 })[0];
|
|
181
|
+
const soup = root.children.find((node) => node.name === "soup");
|
|
182
|
+
expect(solidChildren(soup).length).toBe(0);
|
|
183
|
+
expect(soup.droppedSolids).toBe(SOUP_SOLID_COUNT);
|
|
184
|
+
});
|
|
185
|
+
test("emits small all-unnamed sets with positional names", () => {
|
|
186
|
+
const root = extractStructure(MULTIBODY_FIXTURE, { includeSolids: true })[0];
|
|
187
|
+
const soup = root.children.find((node) => node.name === "soup");
|
|
188
|
+
expect(solidChildren(soup).map((solid) => solid.name))
|
|
189
|
+
.toEqual(["Solid 1 of 3", "Solid 2 of 3", "Solid 3 of 3"]);
|
|
190
|
+
});
|
|
191
|
+
test("caps per-product solids and reports the overflow", () => {
|
|
192
|
+
const root = extractStructure(MULTIBODY_FIXTURE, { includeSolids: true, maxSolidsPerProduct: 2 })[0];
|
|
193
|
+
const widget = root.children.find((node) => node.name === "widget");
|
|
194
|
+
const solids = solidChildren(widget);
|
|
195
|
+
expect(solids.length).toBe(2);
|
|
196
|
+
expect(widget.droppedSolids).toBe(WIDGET_SOLID_COUNT - 2);
|
|
197
|
+
// Truncated, not renumbered: positions stay stable under the cap.
|
|
198
|
+
expect(solids.map((solid) => solid.name)).toEqual(["Body1", "Body2"]);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IfcApiProxyAP214 } from "./ifc_api_proxy_ap214.js";
|
|
2
|
-
import { IncludeProperties, Node } from "./properties_passthrough.js";
|
|
2
|
+
import { IncludeProperties, Node, SpatialStructureOptions } from "./properties_passthrough.js";
|
|
3
3
|
/**
|
|
4
4
|
* web-ifc-compatible property/spatial surface over an AP214/AP242 step model.
|
|
5
5
|
*
|
|
@@ -15,6 +15,7 @@ import { IncludeProperties, Node } from "./properties_passthrough.js";
|
|
|
15
15
|
export declare class AP214Properties {
|
|
16
16
|
private api;
|
|
17
17
|
private structureRoots_?;
|
|
18
|
+
private structureRootsWithSolids_?;
|
|
18
19
|
private propertyMap_?;
|
|
19
20
|
private ownerByExpressID_?;
|
|
20
21
|
private nodeNameByExpressID_?;
|
|
@@ -85,10 +86,13 @@ export declare class AP214Properties {
|
|
|
85
86
|
*
|
|
86
87
|
* @param includeProperties When true, merge each node's item properties onto
|
|
87
88
|
* the node (mirrors the IFC surface's `includeProperties`).
|
|
89
|
+
* @param options Optional shaping: `{ includeSolids: true }` adds the
|
|
90
|
+
* ephemeral solid layer beneath multibody products (`type: 'solid'`,
|
|
91
|
+
* `ephemeral: true` nodes) — see `design/new/step-nonproduct-semantics.md`.
|
|
88
92
|
* @return {Promise<Node>} The root node. A single-root file returns its root
|
|
89
93
|
* directly; a multi-root file is wrapped in a synthetic container root.
|
|
90
94
|
*/
|
|
91
|
-
getSpatialStructure(includeProperties?: IncludeProperties): Promise<Node>;
|
|
95
|
+
getSpatialStructure(includeProperties?: IncludeProperties, options?: SpatialStructureOptions): Promise<Node>;
|
|
92
96
|
/**
|
|
93
97
|
* Get every element of a STEP entity type, backed by the model type index.
|
|
94
98
|
*
|
|
@@ -146,6 +150,16 @@ export declare class AP214Properties {
|
|
|
146
150
|
* @return {ProductStructureNode[]} The cached assembly roots.
|
|
147
151
|
*/
|
|
148
152
|
private productStructure;
|
|
153
|
+
/**
|
|
154
|
+
* Return (and cache) the product-structure forest with the ephemeral solid
|
|
155
|
+
* layer included. Built separately from the plain forest so default callers
|
|
156
|
+
* never pay for the solid walk; the solid nodes are also indexed so
|
|
157
|
+
* `getItemProperties` resolves a solid id to its identity row and
|
|
158
|
+
* `getPropertySets` maps it to the owning product's rows.
|
|
159
|
+
*
|
|
160
|
+
* @return {ProductStructureNode[]} The cached solid-including roots.
|
|
161
|
+
*/
|
|
162
|
+
private productStructureWithSolids;
|
|
149
163
|
/**
|
|
150
164
|
* Lazily build and cache the extracted property map.
|
|
151
165
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ap214_properties.d.ts","sourceRoot":"","sources":["../../../../src/compat/web-ifc/ap214_properties.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,
|
|
1
|
+
{"version":3,"file":"ap214_properties.d.ts","sourceRoot":"","sources":["../../../../src/compat/web-ifc/ap214_properties.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,EACL,iBAAiB,EACjB,IAAI,EACJ,uBAAuB,EACxB,MAAM,0BAA0B,CAAA;AAiHjC;;;;;;;;;;;GAWG;AACH,qBAAa,eAAe;IAYb,OAAO,CAAC,GAAG;IAVxB,OAAO,CAAC,eAAe,CAAC,CAAwB;IAChD,OAAO,CAAC,yBAAyB,CAAC,CAAwB;IAC1D,OAAO,CAAC,YAAY,CAAC,CAAsB;IAC3C,OAAO,CAAC,iBAAiB,CAAC,CAAqB;IAC/C,OAAO,CAAC,oBAAoB,CAAC,CAAqB;IAClD,OAAO,CAAC,iBAAiB,CAAC,CAAgC;IAE1D;;OAEG;gBACkB,GAAG,EAAE,gBAAgB;IAG1C;;;;;;OAMG;IACH,UAAU,CAAE,IAAI,EAAE,MAAM,GAAI,MAAM;IAIlC;;;;;;;;;;;;;;;;;;OAkBG;IACG,iBAAiB,CAAE,EAAE,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAI,OAAO,CAAC,MAAM,CAAC;IAoBzE;;;;;;;;;;;OAWG;IACG,eAAe,CAAE,SAAS,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAkD7E;;;;;;OAMG;IACG,iBAAiB,CAAE,SAAS,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAI/E;;;;;;OAMG;IACG,sBAAsB,CAAE,SAAS,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAIpF;;;;;;;;;;OAUG;IACG,mBAAmB,CACrB,iBAAiB,CAAC,EAAE,iBAAiB,EACrC,OAAO,CAAC,EAAE,uBAAuB,GAAI,OAAO,CAAC,IAAI,CAAC;IAwBtD;;;;;;OAMG;IACG,iBAAiB,CAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAmBzE;;;;;;;OAOG;YACW,aAAa;IAoC3B;;;;;;OAMG;IACH,OAAO,CAAC,cAAc;IAmBtB;;;;;;;;;OASG;IACH;;;;;;OAMG;IACI,YAAY,IAAI,IAAI;IAI3B,OAAO,CAAC,YAAY;IA6BpB;;;;;OAKG;IACH,OAAO,CAAC,UAAU;IAUlB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAKxB;;;;;;;;OAQG;IACH,OAAO,CAAC,0BAA0B;IAkBlC;;;;OAIG;IACH,OAAO,CAAC,UAAU;CAQnB"}
|