@pascal-app/ifc-converter 1.0.3 → 1.0.4
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/README.md +64 -4
- package/dist/beam-geometry.d.ts.map +1 -1
- package/dist/beam-geometry.js +3 -2
- package/dist/cleanup.d.ts +8 -1
- package/dist/cleanup.d.ts.map +1 -1
- package/dist/cleanup.js +13 -4
- package/dist/collections.d.ts +4 -0
- package/dist/collections.d.ts.map +1 -0
- package/dist/collections.js +66 -0
- package/dist/export/door-operation.d.ts +13 -0
- package/dist/export/door-operation.d.ts.map +1 -0
- package/dist/export/door-operation.js +45 -0
- package/dist/export/guid.d.ts +15 -0
- package/dist/export/guid.d.ts.map +1 -0
- package/dist/export/guid.js +80 -0
- package/dist/export/ifc-model.d.ts +87 -0
- package/dist/export/ifc-model.d.ts.map +1 -0
- package/dist/export/ifc-model.js +183 -0
- package/dist/export/index.d.ts +3 -0
- package/dist/export/index.d.ts.map +1 -0
- package/dist/export/index.js +2 -0
- package/dist/export/scene-to-ifc.d.ts +45 -0
- package/dist/export/scene-to-ifc.d.ts.map +1 -0
- package/dist/export/scene-to-ifc.js +1123 -0
- package/dist/export/step.d.ts +64 -0
- package/dist/export/step.d.ts.map +1 -0
- package/dist/export/step.js +158 -0
- package/dist/export/wall-base.d.ts +25 -0
- package/dist/export/wall-base.d.ts.map +1 -0
- package/dist/export/wall-base.js +91 -0
- package/dist/ids.d.ts +3 -0
- package/dist/ids.d.ts.map +1 -0
- package/dist/ids.js +35 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +570 -151
- package/dist/room-first.d.ts +35 -0
- package/dist/room-first.d.ts.map +1 -0
- package/dist/room-first.js +885 -0
- package/dist/wall-joins.d.ts +21 -0
- package/dist/wall-joins.d.ts.map +1 -0
- package/dist/wall-joins.js +367 -0
- package/package.json +8 -4
package/README.md
CHANGED
|
@@ -17,10 +17,11 @@ Native Pascal nodes are produced when the converter can recover the required
|
|
|
17
17
|
parameters for sites, buildings, levels, walls, doors, windows, slabs, columns,
|
|
18
18
|
and IFC spaces (as room zones). IFC stair flights are retained as exact imported
|
|
19
19
|
meshes; roofs retain Pascal hierarchy and source metadata but are not yet a
|
|
20
|
-
complete parametric conversion. Railings, coverings
|
|
21
|
-
curtain walls, plates, members,
|
|
22
|
-
|
|
23
|
-
as selectable `imported-mesh` nodes using
|
|
20
|
+
complete parametric conversion. Railings, coverings other than ceilings and
|
|
21
|
+
flooring, furnishings, proxies, curtain walls, plates, members, footings,
|
|
22
|
+
vegetation (`IfcGeographicElement`), and elements whose native parameters
|
|
23
|
+
cannot be recovered are retained as selectable `imported-mesh` nodes using
|
|
24
|
+
their IFC triangle geometry and color.
|
|
24
25
|
Imported meshes are import-only and do not appear as empty objects in the editor
|
|
25
26
|
palette.
|
|
26
27
|
|
|
@@ -28,5 +29,64 @@ Door families are derived from `IfcDoor.OperationType`. Glazing is applied from
|
|
|
28
29
|
the standardized `Pset_DoorCommon.GlazingAreaFraction` property rather than
|
|
29
30
|
from element names or project-specific conventions.
|
|
30
31
|
|
|
32
|
+
## Room-first structure
|
|
33
|
+
|
|
34
|
+
The importer writes intent for Pascal's room-first model; the scene load
|
|
35
|
+
migrations (room adoption, floor plates) and the structure kernel turn it into
|
|
36
|
+
rooms, floor plates and ceilings. `tests/room-first-load.test.ts` converts the
|
|
37
|
+
reference files and runs that load chain.
|
|
38
|
+
|
|
39
|
+
- **Walls.** Merged collinear fragments, then every straight wall end moves
|
|
40
|
+
along its axis onto the centreline it meets (L and T joins; ends inside
|
|
41
|
+
another wall's body reach its centreline, `IfcRelConnectsPathElements` widens
|
|
42
|
+
the reach). Walls crossing mid-span split; thin cladding (tiles, skirting)
|
|
43
|
+
and walls lining or hidden in another wall stay imported meshes. Openings
|
|
44
|
+
keep their plan position. An `IfcMaterialLayerSetUsage` whose reference line
|
|
45
|
+
is a wall face becomes `justification` (`a`/`b`) with start/end on that face,
|
|
46
|
+
and each junction is rebuilt where the reference lines meet (junctions they
|
|
47
|
+
cannot close stay centred); other offsets are moved to the body centreline.
|
|
48
|
+
- **Rooms.** `IfcSpace` becomes a room zone (`LongName` → name, `Name` →
|
|
49
|
+
room number) with a `seed` at its pole of inaccessibility; it is adopted by
|
|
50
|
+
the wall loop it fills. Spaces sharing one wall loop (open plan, or joined
|
|
51
|
+
through a gap in the walls) get separators along their shared borders or
|
|
52
|
+
across the gap; a space mostly bounded by walls but open on one side (a
|
|
53
|
+
curtain-wall facade) is closed along its open edges. Wall loops no space
|
|
54
|
+
claims become rooms too (`metadata.ifcDerived: 'wall-loop'`).
|
|
55
|
+
- **Floors.** Slab tops are the true walking surface (the extrusion's upper
|
|
56
|
+
end, or the mesh when the body is more than one extrusion) and thickness is
|
|
57
|
+
the IFC thickness. Structural floors (`FLOOR`/`BASESLAB`/unset) whose top
|
|
58
|
+
matches the level's main floor within 2 cm and that floor at least one room
|
|
59
|
+
whole (openings aside) become floor-plate templates: the
|
|
60
|
+
kernel re-derives the plate from the rooms and keeps the IFC top and
|
|
61
|
+
thickness (`referenceFloorElevation`, `foundation`). A part reaching more than
|
|
62
|
+
1 m² / 5 % past the rooms and walls stays a hand-drawn slab (`metadata.ifcSplit:
|
|
63
|
+
'outside-rooms'`). Other slabs stay hand-drawn with their holes, and rooms
|
|
64
|
+
standing on them point at them with `floor.sourceSlabId`; rooms with no slab
|
|
65
|
+
under them have `hasFloor: false`. Holes in room floors, and the unfloored
|
|
66
|
+
part of a room a plate only partly carries, become `floor-opening` nodes.
|
|
67
|
+
Levels are marked `floorOwnershipMigrated` so the load migration's legacy
|
|
68
|
+
floor guesses leave these decisions alone.
|
|
69
|
+
- **Finishes.** Slabs up to 6 cm thick lying on a structural floor inside rooms
|
|
70
|
+
(Revit "Finish Floor" types) and `IfcCovering` `FLOORING` become the room's
|
|
71
|
+
`floor.finish` when one covers most of the room, and `floor.regions` for the
|
|
72
|
+
rest (a library material chosen from the material/type name — wood, tile,
|
|
73
|
+
marble, stone, concrete — else the surface colour), plus the `floorFinish`
|
|
74
|
+
label. A finish slab is removed only when all of it is represented. The
|
|
75
|
+
plate's top is the finish top and its thickness includes the finish.
|
|
76
|
+
- **Ceilings.** `IfcCovering` `CEILING` becomes the automatic ceiling of the
|
|
77
|
+
room it covers, at the covering's underside, keeping the covering's own
|
|
78
|
+
holes (profile voids and `IfcRelVoidsElement` openings). Sloped coverings stay
|
|
79
|
+
imported meshes. When a file models ceilings, a
|
|
80
|
+
room without one gets `hasCeiling: false`; files without ceiling coverings
|
|
81
|
+
keep Pascal's default ceilings except for rooms without a floor.
|
|
82
|
+
- **Doors and windows** carry `wallId` and `floorThresholdVersion: 1`.
|
|
83
|
+
- **Site.** The site polygon is the `IfcSite` footprint when it holds the
|
|
84
|
+
model, else the imported extent plus 5 m.
|
|
85
|
+
- **Pascal round trip.** Elements carrying a `Pascal` property set (written by
|
|
86
|
+
`@pascal-app/ifc-converter/export`) keep their node id (imported meshes
|
|
87
|
+
included) and skip wall cleanup
|
|
88
|
+
(merging, joins, duplicate-opening removal); storeys read the `GrossHeight`
|
|
89
|
+
quantity for the top storey's height.
|
|
90
|
+
|
|
31
91
|
Browser callers use the default WebIFC WASM path (`/`). Node callers can pass
|
|
32
92
|
`{ wasmPath: '/absolute/path/to/web-ifc/' }` in `ConversionOptions`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"beam-geometry.d.ts","sourceRoot":"","sources":["../src/beam-geometry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAA0B,MAAM,kBAAkB,CAAA;AAC7E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAErC,KAAK,KAAK,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAErC,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GACzF;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,aAAa,CAAA;CAAE,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"beam-geometry.d.ts","sourceRoot":"","sources":["../src/beam-geometry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAA0B,MAAM,kBAAkB,CAAA;AAC7E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAErC,KAAK,KAAK,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAErC,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GACzF;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,aAAa,CAAA;CAAE,GAAG,IAAI,CA0FrD"}
|
package/dist/beam-geometry.js
CHANGED
|
@@ -25,7 +25,8 @@ export function extractBeamGeometry(ifcApi, modelID, expressID, options) {
|
|
|
25
25
|
const sx = wx - options.origin[0] * options.unitFactor;
|
|
26
26
|
const sy = -wz - options.origin[1] * options.unitFactor;
|
|
27
27
|
const sz = wy - options.origin[2] * options.unitFactor - options.levelElevation;
|
|
28
|
-
|
|
28
|
+
// Pascal is Y-up right-handed: IFC north (+Y) is Pascal -Z.
|
|
29
|
+
const position = options.swapYZ ? [sx, sz, -sy] : [sx, sy, sz];
|
|
29
30
|
if (!position.every(Number.isFinite))
|
|
30
31
|
throw new Error('Non-finite beam vertex');
|
|
31
32
|
const key = position.join(',');
|
|
@@ -41,7 +42,7 @@ export function extractBeamGeometry(ifcApi, modelID, expressID, options) {
|
|
|
41
42
|
m[4] * (m[1] * m[10] - m[9] * m[2]) +
|
|
42
43
|
m[8] * (m[1] * m[6] - m[5] * m[2]);
|
|
43
44
|
// Baking a reflection into the vertices also reverses the outward face winding.
|
|
44
|
-
const reverseWinding = determinant < 0
|
|
45
|
+
const reverseWinding = determinant < 0;
|
|
45
46
|
for (let i = 0; i + 2 < indices.length; i += 3) {
|
|
46
47
|
const ids = [vertexIds[indices[i]], vertexIds[indices[i + 1]], vertexIds[indices[i + 2]]];
|
|
47
48
|
if (ids.some((id) => id === undefined))
|
package/dist/cleanup.d.ts
CHANGED
|
@@ -19,7 +19,14 @@ export type IfcConversionSimplificationStats = {
|
|
|
19
19
|
mergedWallGroups: number;
|
|
20
20
|
removedMergedWalls: number;
|
|
21
21
|
removedDuplicateOpenings: number;
|
|
22
|
+
joinedWallEnds: number;
|
|
23
|
+
splitCrossingWalls: number;
|
|
22
24
|
};
|
|
23
|
-
export
|
|
25
|
+
/** Walls and openings a Pascal export wrote are kept exactly as they were authored. */
|
|
26
|
+
export declare function isPascalAuthored(node: AnyNode | undefined): boolean;
|
|
27
|
+
export declare function simplifyConvertedSceneGraph(nodes: SceneNodes, options?: IfcConversionSimplificationOptions, context?: {
|
|
28
|
+
wallConnections?: readonly (readonly [number, number])[];
|
|
29
|
+
wallLinings?: ReadonlyMap<number, number>;
|
|
30
|
+
}): IfcConversionSimplificationStats;
|
|
24
31
|
export {};
|
|
25
32
|
//# sourceMappingURL=cleanup.d.ts.map
|
package/dist/cleanup.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../src/cleanup.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EAMb,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../src/cleanup.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EAMb,MAAM,kBAAkB,CAAA;AAGzB,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAGzC,MAAM,MAAM,kCAAkC,GAAG;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,gCAAgC,GAAG;IAC7C,KAAK,EAAE;QACL,KAAK,EAAE,MAAM,CAAA;QACb,KAAK,EAAE,MAAM,CAAA;QACb,OAAO,EAAE,MAAM,CAAA;KAChB,CAAA;IACD,MAAM,EAAE;QACN,KAAK,EAAE,MAAM,CAAA;QACb,KAAK,EAAE,MAAM,CAAA;QACb,OAAO,EAAE,MAAM,CAAA;KAChB,CAAA;IACD,gBAAgB,EAAE,MAAM,CAAA;IACxB,gBAAgB,EAAE,MAAM,CAAA;IACxB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,wBAAwB,EAAE,MAAM,CAAA;IAChC,cAAc,EAAE,MAAM,CAAA;IACtB,kBAAkB,EAAE,MAAM,CAAA;CAC3B,CAAA;AA0DD,uFAAuF;AACvF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,SAAS,WAIzD;AAgbD,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,UAAU,EACjB,OAAO,GAAE,kCAAuC,EAChD,OAAO,GAAE;IACP,eAAe,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,CAAA;IACxD,WAAW,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACrC,GACL,gCAAgC,CAsBlC"}
|
package/dist/cleanup.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DEFAULT_WALL_HEIGHT, DEFAULT_WALL_THICKNESS, } from '@pascal-app/core';
|
|
2
|
+
import { joinWallEnds, splitCrossingWalls } from './wall-joins';
|
|
2
3
|
const MIN_WALL_LENGTH = 0.08;
|
|
3
4
|
const WALL_ANGLE_BUCKET_RAD = Math.PI / 180;
|
|
4
5
|
const DEFAULT_MAX_WALL_JOIN_GAP = 1.25;
|
|
@@ -23,6 +24,8 @@ function getInitialStats(nodes) {
|
|
|
23
24
|
mergedWallGroups: 0,
|
|
24
25
|
removedMergedWalls: 0,
|
|
25
26
|
removedDuplicateOpenings: 0,
|
|
27
|
+
joinedWallEnds: 0,
|
|
28
|
+
splitCrossingWalls: 0,
|
|
26
29
|
};
|
|
27
30
|
}
|
|
28
31
|
function finishStats(nodes, stats) {
|
|
@@ -32,6 +35,10 @@ function finishStats(nodes, stats) {
|
|
|
32
35
|
windows: countNodes(nodes, 'window'),
|
|
33
36
|
};
|
|
34
37
|
}
|
|
38
|
+
/** Walls and openings a Pascal export wrote are kept exactly as they were authored. */
|
|
39
|
+
export function isPascalAuthored(node) {
|
|
40
|
+
return (typeof node?.metadata?.pascalNodeId === 'string');
|
|
41
|
+
}
|
|
35
42
|
function isOpeningNode(node) {
|
|
36
43
|
return node?.type === 'door' || node?.type === 'window';
|
|
37
44
|
}
|
|
@@ -92,7 +99,7 @@ function wallLength(wall) {
|
|
|
92
99
|
}
|
|
93
100
|
function pruneTinyWalls(nodes, stats) {
|
|
94
101
|
for (const node of Object.values(nodes)) {
|
|
95
|
-
if (node.type !== 'wall')
|
|
102
|
+
if (node.type !== 'wall' || isPascalAuthored(node))
|
|
96
103
|
continue;
|
|
97
104
|
if (wallLength(node) >= MIN_WALL_LENGTH)
|
|
98
105
|
continue;
|
|
@@ -352,7 +359,7 @@ function mergeWallCluster(nodes, cluster, stats) {
|
|
|
352
359
|
}
|
|
353
360
|
function mergeWallFragments(nodes, stats, options) {
|
|
354
361
|
const segments = Object.values(nodes)
|
|
355
|
-
.filter((node) => node.type === 'wall')
|
|
362
|
+
.filter((node) => node.type === 'wall' && !isPascalAuthored(node))
|
|
356
363
|
.map(toWallSegment)
|
|
357
364
|
.filter((segment) => segment !== null);
|
|
358
365
|
const groups = new Map();
|
|
@@ -412,7 +419,7 @@ function openingSignature(opening) {
|
|
|
412
419
|
function dedupeOpenings(nodes, stats) {
|
|
413
420
|
const byWall = new Map();
|
|
414
421
|
for (const node of Object.values(nodes)) {
|
|
415
|
-
if (!isOpeningNode(node))
|
|
422
|
+
if (!isOpeningNode(node) || isPascalAuthored(node))
|
|
416
423
|
continue;
|
|
417
424
|
const wallId = getOpeningWallId(node, nodes);
|
|
418
425
|
if (!wallId)
|
|
@@ -437,7 +444,7 @@ function dedupeOpenings(nodes, stats) {
|
|
|
437
444
|
}
|
|
438
445
|
}
|
|
439
446
|
}
|
|
440
|
-
export function simplifyConvertedSceneGraph(nodes, options = {}) {
|
|
447
|
+
export function simplifyConvertedSceneGraph(nodes, options = {}, context = {}) {
|
|
441
448
|
const stats = getInitialStats(nodes);
|
|
442
449
|
if (options.enabled === false) {
|
|
443
450
|
finishStats(nodes, stats);
|
|
@@ -450,6 +457,8 @@ export function simplifyConvertedSceneGraph(nodes, options = {}) {
|
|
|
450
457
|
pruneTinyWalls(nodes, stats);
|
|
451
458
|
mergeWallFragments(nodes, stats, resolvedOptions);
|
|
452
459
|
syncWallOpeningChildren(nodes);
|
|
460
|
+
stats.joinedWallEnds = joinWallEnds(nodes, context.wallConnections, context.wallLinings);
|
|
461
|
+
stats.splitCrossingWalls = splitCrossingWalls(nodes);
|
|
453
462
|
dedupeOpenings(nodes, stats);
|
|
454
463
|
normalizeChildren(nodes);
|
|
455
464
|
finishStats(nodes, stats);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type AnyNode, type Collection, type CollectionId } from '@pascal-app/core';
|
|
2
|
+
import * as WebIFC from 'web-ifc';
|
|
3
|
+
export declare function importCollections(api: WebIFC.IfcAPI, modelID: number, nodes: Record<string, AnyNode>, nodeIds: ReadonlyMap<number, string>): Record<CollectionId, Collection>;
|
|
4
|
+
//# sourceMappingURL=collections.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collections.d.ts","sourceRoot":"","sources":["../src/collections.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EAGZ,KAAK,UAAU,EACf,KAAK,YAAY,EAGlB,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,MAAM,MAAM,SAAS,CAAA;AAEjC,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,MAAM,CAAC,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GACnC,MAAM,CAAC,YAAY,EAAE,UAAU,CAAC,CA0DlC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { COLLECTION_TEMPLATES, generateCollectionId, } from '@pascal-app/core';
|
|
2
|
+
import * as WebIFC from 'web-ifc';
|
|
3
|
+
export function importCollections(api, modelID, nodes, nodeIds) {
|
|
4
|
+
const groups = api.GetLineIDsWithType(modelID, WebIFC.IFCGROUP, true);
|
|
5
|
+
if (groups.size() === 0)
|
|
6
|
+
return {};
|
|
7
|
+
const properties = new Map();
|
|
8
|
+
const definitions = api.GetLineIDsWithType(modelID, WebIFC.IFCRELDEFINESBYPROPERTIES);
|
|
9
|
+
for (let i = 0; i < definitions.size(); i++) {
|
|
10
|
+
const rel = api.GetLine(modelID, definitions.get(i));
|
|
11
|
+
if (!rel.RelatingPropertyDefinition?.value)
|
|
12
|
+
continue;
|
|
13
|
+
const definition = api.GetLine(modelID, rel.RelatingPropertyDefinition.value);
|
|
14
|
+
if (definition?.Name?.value !== 'PascalCollection')
|
|
15
|
+
continue;
|
|
16
|
+
const values = {};
|
|
17
|
+
for (const ref of definition.HasProperties ?? []) {
|
|
18
|
+
const property = api.GetLine(modelID, ref.value);
|
|
19
|
+
if (property?.Name?.value)
|
|
20
|
+
values[property.Name.value] = property.NominalValue?.value;
|
|
21
|
+
}
|
|
22
|
+
for (const ref of rel.RelatedObjects ?? [])
|
|
23
|
+
properties.set(ref.value, values);
|
|
24
|
+
}
|
|
25
|
+
const members = new Map();
|
|
26
|
+
const assignments = api.GetLineIDsWithType(modelID, WebIFC.IFCRELASSIGNSTOGROUP, true);
|
|
27
|
+
for (let i = 0; i < assignments.size(); i++) {
|
|
28
|
+
const rel = api.GetLine(modelID, assignments.get(i));
|
|
29
|
+
const groupId = rel.RelatingGroup?.value;
|
|
30
|
+
if (!groupId)
|
|
31
|
+
continue;
|
|
32
|
+
const ids = members.get(groupId) ?? new Set();
|
|
33
|
+
for (const ref of rel.RelatedObjects ?? []) {
|
|
34
|
+
const id = nodeIds.get(ref.value);
|
|
35
|
+
if (id && nodes[id])
|
|
36
|
+
ids.add(id);
|
|
37
|
+
}
|
|
38
|
+
members.set(groupId, ids);
|
|
39
|
+
}
|
|
40
|
+
const collections = {};
|
|
41
|
+
for (let i = 0; i < groups.size(); i++) {
|
|
42
|
+
const expressId = groups.get(i);
|
|
43
|
+
const ids = members.get(expressId);
|
|
44
|
+
if (!ids?.size)
|
|
45
|
+
continue;
|
|
46
|
+
const group = api.GetLine(modelID, expressId);
|
|
47
|
+
const values = properties.get(expressId);
|
|
48
|
+
const storedId = values?.CollectionId;
|
|
49
|
+
const id = typeof storedId === 'string' &&
|
|
50
|
+
storedId.startsWith('collection_') &&
|
|
51
|
+
!Object.hasOwn(collections, storedId)
|
|
52
|
+
? storedId
|
|
53
|
+
: generateCollectionId();
|
|
54
|
+
const template = values?.Template;
|
|
55
|
+
collections[id] = {
|
|
56
|
+
id,
|
|
57
|
+
name: (typeof group.Name?.value === 'string' && group.Name.value.trim()) || 'IFC group',
|
|
58
|
+
nodeIds: [...ids],
|
|
59
|
+
...(typeof template === 'string' && Object.hasOwn(COLLECTION_TEMPLATES, template)
|
|
60
|
+
? { template: template }
|
|
61
|
+
: {}),
|
|
62
|
+
...(typeof values?.Color === 'string' ? { color: values.Color } : {}),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return collections;
|
|
66
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DoorNode } from '@pascal-app/core';
|
|
2
|
+
/**
|
|
3
|
+
* Pascal door family → IfcDoorTypeOperationEnum. The inverse of
|
|
4
|
+
* `doorStyleFromIfcOperation` (door-semantics.ts), so an exported door
|
|
5
|
+
* re-imports as the same family.
|
|
6
|
+
*/
|
|
7
|
+
export declare function doorOperationForIfc(door: DoorNode): {
|
|
8
|
+
operationType: string;
|
|
9
|
+
userDefined?: string;
|
|
10
|
+
};
|
|
11
|
+
/** Share of the leaf height that is glazed, for Pset_DoorCommon.GlazingAreaFraction. */
|
|
12
|
+
export declare function doorGlazingFraction(door: DoorNode): number;
|
|
13
|
+
//# sourceMappingURL=door-operation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"door-operation.d.ts","sourceRoot":"","sources":["../../src/export/door-operation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAEhD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,GAAG;IACnD,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CA0BA;AAED,wFAAwF;AACxF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAQ1D"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pascal door family → IfcDoorTypeOperationEnum. The inverse of
|
|
3
|
+
* `doorStyleFromIfcOperation` (door-semantics.ts), so an exported door
|
|
4
|
+
* re-imports as the same family.
|
|
5
|
+
*/
|
|
6
|
+
export function doorOperationForIfc(door) {
|
|
7
|
+
const doubleLeaf = (door.leafCount ?? 1) >= 2;
|
|
8
|
+
switch (door.doorType) {
|
|
9
|
+
case 'double':
|
|
10
|
+
case 'french':
|
|
11
|
+
return { operationType: 'DOUBLE_DOOR_SINGLE_SWING' };
|
|
12
|
+
case 'sliding':
|
|
13
|
+
case 'pocket':
|
|
14
|
+
case 'barn':
|
|
15
|
+
if (doubleLeaf)
|
|
16
|
+
return { operationType: 'DOUBLE_DOOR_SLIDING' };
|
|
17
|
+
return {
|
|
18
|
+
operationType: door.slideDirection === 'right' ? 'SLIDING_TO_RIGHT' : 'SLIDING_TO_LEFT',
|
|
19
|
+
};
|
|
20
|
+
case 'folding':
|
|
21
|
+
return { operationType: doubleLeaf ? 'DOUBLE_DOOR_FOLDING' : 'FOLDING_TO_LEFT' };
|
|
22
|
+
case 'garage-rollup':
|
|
23
|
+
return { operationType: 'ROLLINGUP' };
|
|
24
|
+
case 'garage-sectional':
|
|
25
|
+
case 'garage-tiltup':
|
|
26
|
+
return { operationType: 'USERDEFINED', userDefined: door.doorType };
|
|
27
|
+
default:
|
|
28
|
+
if (doubleLeaf)
|
|
29
|
+
return { operationType: 'DOUBLE_DOOR_SINGLE_SWING' };
|
|
30
|
+
return {
|
|
31
|
+
operationType: door.hingesSide === 'right' ? 'SINGLE_SWING_RIGHT' : 'SINGLE_SWING_LEFT',
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Share of the leaf height that is glazed, for Pset_DoorCommon.GlazingAreaFraction. */
|
|
36
|
+
export function doorGlazingFraction(door) {
|
|
37
|
+
const segments = door.segments ?? [];
|
|
38
|
+
const total = segments.reduce((sum, segment) => sum + Math.max(0, segment.heightRatio), 0);
|
|
39
|
+
const glass = segments
|
|
40
|
+
.filter((segment) => segment.type === 'glass')
|
|
41
|
+
.reduce((sum, segment) => sum + Math.max(0, segment.heightRatio), 0);
|
|
42
|
+
if (total > 0 && glass > 0)
|
|
43
|
+
return Math.min(1, glass / total);
|
|
44
|
+
return door.doorType === 'french' ? 1 : 0;
|
|
45
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare function isIfcGuid(value: unknown): value is string;
|
|
2
|
+
/** Compress 16 GUID bytes into IFC's 22-character base-64 form. */
|
|
3
|
+
export declare function compressGuidBytes(bytes: Uint8Array): string;
|
|
4
|
+
/** Deterministic IFC GlobalId for a seed (a Pascal node id plus an optional role suffix). */
|
|
5
|
+
export declare function ifcGuidFromSeed(seed: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Hands out GlobalIds, reusing an imported element's original GUID the first
|
|
8
|
+
* time it is seen. Duplicated nodes copy metadata, so a second claim on the
|
|
9
|
+
* same imported GUID falls back to the node-derived one to stay unique.
|
|
10
|
+
*/
|
|
11
|
+
export declare class GuidRegistry {
|
|
12
|
+
private readonly used;
|
|
13
|
+
claim(seed: string, preferred?: unknown): string;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=guid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guid.d.ts","sourceRoot":"","sources":["../../src/export/guid.ts"],"names":[],"mappings":"AAGA,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAEzD;AA+CD,mEAAmE;AACnE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAS3D;AAED,6FAA6F;AAC7F,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;;;GAIG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoB;IAEzC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM;CAYjD"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const IFC_GUID_ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_$';
|
|
2
|
+
const IFC_GUID_PATTERN = /^[0-3][0-9A-Za-z_$]{21}$/;
|
|
3
|
+
export function isIfcGuid(value) {
|
|
4
|
+
return typeof value === 'string' && IFC_GUID_PATTERN.test(value);
|
|
5
|
+
}
|
|
6
|
+
// cyrb128: a fast synchronous 128-bit string hash. Web Crypto digests are
|
|
7
|
+
// async-only in browsers, and the GUID only needs to be stable and
|
|
8
|
+
// collision-resistant across one model, not cryptographically strong.
|
|
9
|
+
function hash128(input) {
|
|
10
|
+
let h1 = 1779033703;
|
|
11
|
+
let h2 = 3144134277;
|
|
12
|
+
let h3 = 1013904242;
|
|
13
|
+
let h4 = 2773480762;
|
|
14
|
+
for (let i = 0; i < input.length; i++) {
|
|
15
|
+
const k = input.charCodeAt(i);
|
|
16
|
+
h1 = h2 ^ Math.imul(h1 ^ k, 597399067);
|
|
17
|
+
h2 = h3 ^ Math.imul(h2 ^ k, 2869860233);
|
|
18
|
+
h3 = h4 ^ Math.imul(h3 ^ k, 951274213);
|
|
19
|
+
h4 = h1 ^ Math.imul(h4 ^ k, 2716044179);
|
|
20
|
+
}
|
|
21
|
+
h1 = Math.imul(h3 ^ (h1 >>> 18), 597399067);
|
|
22
|
+
h2 = Math.imul(h4 ^ (h2 >>> 22), 2869860233);
|
|
23
|
+
h3 = Math.imul(h1 ^ (h3 >>> 17), 951274213);
|
|
24
|
+
h4 = Math.imul(h2 ^ (h4 >>> 19), 2716044179);
|
|
25
|
+
h1 ^= h2 ^ h3 ^ h4;
|
|
26
|
+
h2 ^= h1;
|
|
27
|
+
h3 ^= h1;
|
|
28
|
+
h4 ^= h1;
|
|
29
|
+
const bytes = new Uint8Array(16);
|
|
30
|
+
const view = new DataView(bytes.buffer);
|
|
31
|
+
view.setUint32(0, h1 >>> 0);
|
|
32
|
+
view.setUint32(4, h2 >>> 0);
|
|
33
|
+
view.setUint32(8, h3 >>> 0);
|
|
34
|
+
view.setUint32(12, h4 >>> 0);
|
|
35
|
+
// Mark as an RFC 4122 name-based (v5-style) UUID so tools that validate
|
|
36
|
+
// the underlying GUID accept it.
|
|
37
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x50;
|
|
38
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
39
|
+
return bytes;
|
|
40
|
+
}
|
|
41
|
+
function toBase64Digits(value, digits) {
|
|
42
|
+
let out = '';
|
|
43
|
+
for (let i = 0; i < digits; i++) {
|
|
44
|
+
out = IFC_GUID_ALPHABET[value % 64] + out;
|
|
45
|
+
value = Math.floor(value / 64);
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
/** Compress 16 GUID bytes into IFC's 22-character base-64 form. */
|
|
50
|
+
export function compressGuidBytes(bytes) {
|
|
51
|
+
let out = toBase64Digits(bytes[0], 2);
|
|
52
|
+
for (let offset = 1; offset < 16; offset += 3) {
|
|
53
|
+
out += toBase64Digits((bytes[offset] << 16) | (bytes[offset + 1] << 8) | bytes[offset + 2], 4);
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
/** Deterministic IFC GlobalId for a seed (a Pascal node id plus an optional role suffix). */
|
|
58
|
+
export function ifcGuidFromSeed(seed) {
|
|
59
|
+
return compressGuidBytes(hash128(`pascal:${seed}`));
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Hands out GlobalIds, reusing an imported element's original GUID the first
|
|
63
|
+
* time it is seen. Duplicated nodes copy metadata, so a second claim on the
|
|
64
|
+
* same imported GUID falls back to the node-derived one to stay unique.
|
|
65
|
+
*/
|
|
66
|
+
export class GuidRegistry {
|
|
67
|
+
used = new Set();
|
|
68
|
+
claim(seed, preferred) {
|
|
69
|
+
if (isIfcGuid(preferred) && !this.used.has(preferred)) {
|
|
70
|
+
this.used.add(preferred);
|
|
71
|
+
return preferred;
|
|
72
|
+
}
|
|
73
|
+
let guid = ifcGuidFromSeed(seed);
|
|
74
|
+
for (let attempt = 1; this.used.has(guid); attempt++) {
|
|
75
|
+
guid = ifcGuidFromSeed(`${seed}#${attempt}`);
|
|
76
|
+
}
|
|
77
|
+
this.used.add(guid);
|
|
78
|
+
return guid;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { GuidRegistry } from './guid';
|
|
2
|
+
import { DERIVED, enumValue, int, type StepRef, type StepValue, StepWriter } from './step';
|
|
3
|
+
export type Vec2 = [number, number];
|
|
4
|
+
export type Vec3 = [number, number, number];
|
|
5
|
+
/** A right-handed IFC frame: origin plus a rotation about +Z (plan angle, radians). */
|
|
6
|
+
export interface PlanFrame {
|
|
7
|
+
origin: Vec3;
|
|
8
|
+
angle: number;
|
|
9
|
+
}
|
|
10
|
+
export declare const IDENTITY_FRAME: PlanFrame;
|
|
11
|
+
export declare function frameToLocal(frame: PlanFrame, point: Vec3): Vec3;
|
|
12
|
+
/** `child` expressed relative to `parent` (both in the same parent space). */
|
|
13
|
+
export declare function relativeFrame(parent: PlanFrame, child: PlanFrame): PlanFrame;
|
|
14
|
+
export declare function signedArea(points: readonly Vec2[]): number;
|
|
15
|
+
/** Drop repeated and closing vertices; returns [] for degenerate rings. */
|
|
16
|
+
export declare function cleanRing(points: readonly Vec2[]): Vec2[];
|
|
17
|
+
export declare function orientRing(points: Vec2[], counterClockwise: boolean): Vec2[];
|
|
18
|
+
export interface IfcColor {
|
|
19
|
+
rgb: Vec3;
|
|
20
|
+
opacity: number;
|
|
21
|
+
}
|
|
22
|
+
export interface TriangleSet {
|
|
23
|
+
/** Local coordinates, flat XYZ. */
|
|
24
|
+
positions: ArrayLike<number>;
|
|
25
|
+
indices: ArrayLike<number>;
|
|
26
|
+
color?: IfcColor;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* IFC4 entity builders shared by the scene mapper. Keeps attribute order for
|
|
30
|
+
* each entity in one place; the mapper only deals with Pascal semantics.
|
|
31
|
+
*/
|
|
32
|
+
export declare class IfcModel {
|
|
33
|
+
readonly step: StepWriter;
|
|
34
|
+
readonly guids: GuidRegistry;
|
|
35
|
+
ownerHistory: StepRef | null;
|
|
36
|
+
bodyContext: StepRef;
|
|
37
|
+
axisContext: StepRef;
|
|
38
|
+
private readonly styleCache;
|
|
39
|
+
guid(seed: string, preferred?: unknown): string;
|
|
40
|
+
point2(p: Vec2): StepRef;
|
|
41
|
+
point3(p: Vec3): StepRef;
|
|
42
|
+
direction(d: readonly number[]): StepRef;
|
|
43
|
+
axis2Placement3D(frame: PlanFrame): StepRef;
|
|
44
|
+
localPlacement(relativeTo: StepRef | null, frame: PlanFrame): StepRef;
|
|
45
|
+
polyline2(points: readonly Vec2[], closed: boolean): StepRef;
|
|
46
|
+
/** Outer ring CCW, holes CW; rings must already be cleaned. */
|
|
47
|
+
polygonProfile(outer: Vec2[], holes?: Vec2[][]): StepRef;
|
|
48
|
+
rectangleProfile(xDim: number, yDim: number, center?: Vec2): StepRef;
|
|
49
|
+
circleProfile(radius: number): StepRef;
|
|
50
|
+
/** Vertical prism of `profile`, from local z = `bottom` upwards by `depth`. */
|
|
51
|
+
extrusion(profile: StepRef, bottom: number, depth: number, color?: IfcColor): StepRef;
|
|
52
|
+
triangulatedFaceSet(set: TriangleSet): StepRef | null;
|
|
53
|
+
private surfaceStyle;
|
|
54
|
+
styleItem(item: StepRef, color: IfcColor): void;
|
|
55
|
+
shape(representations: StepRef[]): StepRef;
|
|
56
|
+
bodyRepresentation(type: 'SweptSolid' | 'Tessellation', items: StepRef[]): StepRef;
|
|
57
|
+
axisRepresentation(curve: StepRef): StepRef;
|
|
58
|
+
rel(type: string, seed: string, ...args: StepValue[]): StepRef;
|
|
59
|
+
propertySet(seed: string, name: string, properties: Array<[string, StepValue]>, relatedObjects: StepRef[]): void;
|
|
60
|
+
lengthQuantities(seed: string, name: string, quantities: Array<[string, number]>, relatedObjects: StepRef[]): void;
|
|
61
|
+
}
|
|
62
|
+
export declare const label: (value: string) => {
|
|
63
|
+
readonly type: string;
|
|
64
|
+
readonly value: StepValue;
|
|
65
|
+
};
|
|
66
|
+
export declare const identifier: (value: string) => {
|
|
67
|
+
readonly type: string;
|
|
68
|
+
readonly value: StepValue;
|
|
69
|
+
};
|
|
70
|
+
export declare const text: (value: string) => {
|
|
71
|
+
readonly type: string;
|
|
72
|
+
readonly value: StepValue;
|
|
73
|
+
};
|
|
74
|
+
export declare const bool: (value: boolean) => {
|
|
75
|
+
readonly type: string;
|
|
76
|
+
readonly value: StepValue;
|
|
77
|
+
};
|
|
78
|
+
export declare const length: (value: number) => {
|
|
79
|
+
readonly type: string;
|
|
80
|
+
readonly value: StepValue;
|
|
81
|
+
};
|
|
82
|
+
export declare const ratio: (value: number) => {
|
|
83
|
+
readonly type: string;
|
|
84
|
+
readonly value: StepValue;
|
|
85
|
+
};
|
|
86
|
+
export { DERIVED, enumValue, int };
|
|
87
|
+
//# sourceMappingURL=ifc-model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ifc-model.d.ts","sourceRoot":"","sources":["../../src/export/ifc-model.ts"],"names":[],"mappings":"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"}
|