@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.
Files changed (43) hide show
  1. package/README.md +64 -4
  2. package/dist/beam-geometry.d.ts.map +1 -1
  3. package/dist/beam-geometry.js +3 -2
  4. package/dist/cleanup.d.ts +8 -1
  5. package/dist/cleanup.d.ts.map +1 -1
  6. package/dist/cleanup.js +13 -4
  7. package/dist/collections.d.ts +4 -0
  8. package/dist/collections.d.ts.map +1 -0
  9. package/dist/collections.js +66 -0
  10. package/dist/export/door-operation.d.ts +13 -0
  11. package/dist/export/door-operation.d.ts.map +1 -0
  12. package/dist/export/door-operation.js +45 -0
  13. package/dist/export/guid.d.ts +15 -0
  14. package/dist/export/guid.d.ts.map +1 -0
  15. package/dist/export/guid.js +80 -0
  16. package/dist/export/ifc-model.d.ts +87 -0
  17. package/dist/export/ifc-model.d.ts.map +1 -0
  18. package/dist/export/ifc-model.js +183 -0
  19. package/dist/export/index.d.ts +3 -0
  20. package/dist/export/index.d.ts.map +1 -0
  21. package/dist/export/index.js +2 -0
  22. package/dist/export/scene-to-ifc.d.ts +45 -0
  23. package/dist/export/scene-to-ifc.d.ts.map +1 -0
  24. package/dist/export/scene-to-ifc.js +1123 -0
  25. package/dist/export/step.d.ts +64 -0
  26. package/dist/export/step.d.ts.map +1 -0
  27. package/dist/export/step.js +158 -0
  28. package/dist/export/wall-base.d.ts +25 -0
  29. package/dist/export/wall-base.d.ts.map +1 -0
  30. package/dist/export/wall-base.js +91 -0
  31. package/dist/ids.d.ts +3 -0
  32. package/dist/ids.d.ts.map +1 -0
  33. package/dist/ids.js +35 -0
  34. package/dist/index.d.ts +2 -2
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +570 -151
  37. package/dist/room-first.d.ts +35 -0
  38. package/dist/room-first.d.ts.map +1 -0
  39. package/dist/room-first.js +885 -0
  40. package/dist/wall-joins.d.ts +21 -0
  41. package/dist/wall-joins.d.ts.map +1 -0
  42. package/dist/wall-joins.js +367 -0
  43. package/package.json +8 -4
@@ -0,0 +1,183 @@
1
+ import { GuidRegistry } from './guid';
2
+ import { DERIVED, enumValue, formatReal, int, raw, StepWriter, typed, } from './step';
3
+ export const IDENTITY_FRAME = { origin: [0, 0, 0], angle: 0 };
4
+ export function frameToLocal(frame, point) {
5
+ const dx = point[0] - frame.origin[0];
6
+ const dy = point[1] - frame.origin[1];
7
+ const c = Math.cos(frame.angle);
8
+ const s = Math.sin(frame.angle);
9
+ return [dx * c + dy * s, -dx * s + dy * c, point[2] - frame.origin[2]];
10
+ }
11
+ /** `child` expressed relative to `parent` (both in the same parent space). */
12
+ export function relativeFrame(parent, child) {
13
+ return { origin: frameToLocal(parent, child.origin), angle: child.angle - parent.angle };
14
+ }
15
+ export function signedArea(points) {
16
+ let area = 0;
17
+ for (let i = 0; i < points.length; i++) {
18
+ const a = points[i];
19
+ const b = points[(i + 1) % points.length];
20
+ area += a[0] * b[1] - b[0] * a[1];
21
+ }
22
+ return area / 2;
23
+ }
24
+ /** Drop repeated and closing vertices; returns [] for degenerate rings. */
25
+ export function cleanRing(points) {
26
+ const out = [];
27
+ for (const point of points) {
28
+ const last = out[out.length - 1];
29
+ if (last && Math.hypot(point[0] - last[0], point[1] - last[1]) < 1e-6)
30
+ continue;
31
+ out.push(point);
32
+ }
33
+ while (out.length > 1) {
34
+ const first = out[0];
35
+ const last = out[out.length - 1];
36
+ if (Math.hypot(first[0] - last[0], first[1] - last[1]) >= 1e-6)
37
+ break;
38
+ out.pop();
39
+ }
40
+ return out.length >= 3 && Math.abs(signedArea(out)) > 1e-9 ? out : [];
41
+ }
42
+ export function orientRing(points, counterClockwise) {
43
+ return signedArea(points) > 0 === counterClockwise ? points : [...points].reverse();
44
+ }
45
+ const MESH_DECIMALS = 5;
46
+ /**
47
+ * IFC4 entity builders shared by the scene mapper. Keeps attribute order for
48
+ * each entity in one place; the mapper only deals with Pascal semantics.
49
+ */
50
+ export class IfcModel {
51
+ step = new StepWriter();
52
+ guids = new GuidRegistry();
53
+ ownerHistory = null;
54
+ bodyContext;
55
+ axisContext;
56
+ styleCache = new Map();
57
+ guid(seed, preferred) {
58
+ return this.guids.claim(seed, preferred);
59
+ }
60
+ point2(p) {
61
+ return this.step.addShared('IFCCARTESIANPOINT', [p[0], p[1]]);
62
+ }
63
+ point3(p) {
64
+ return this.step.addShared('IFCCARTESIANPOINT', [p[0], p[1], p[2]]);
65
+ }
66
+ direction(d) {
67
+ return this.step.addShared('IFCDIRECTION', d.map((v) => Math.round(v * 1e12) / 1e12));
68
+ }
69
+ axis2Placement3D(frame) {
70
+ const identityRotation = Math.abs(frame.angle) < 1e-12;
71
+ return this.step.add('IFCAXIS2PLACEMENT3D', this.point3(frame.origin), identityRotation ? null : this.direction([0, 0, 1]), identityRotation ? null : this.direction([Math.cos(frame.angle), Math.sin(frame.angle), 0]));
72
+ }
73
+ localPlacement(relativeTo, frame) {
74
+ return this.step.add('IFCLOCALPLACEMENT', relativeTo, this.axis2Placement3D(frame));
75
+ }
76
+ polyline2(points, closed) {
77
+ const refs = points.map((p) => this.point2(p));
78
+ if (closed && refs.length > 0)
79
+ refs.push(refs[0]);
80
+ return this.step.add('IFCPOLYLINE', refs);
81
+ }
82
+ /** Outer ring CCW, holes CW; rings must already be cleaned. */
83
+ polygonProfile(outer, holes = []) {
84
+ const outerCurve = this.polyline2(orientRing(outer, true), true);
85
+ if (holes.length === 0) {
86
+ return this.step.add('IFCARBITRARYCLOSEDPROFILEDEF', enumValue('AREA'), null, outerCurve);
87
+ }
88
+ return this.step.add('IFCARBITRARYPROFILEDEFWITHVOIDS', enumValue('AREA'), null, outerCurve, holes.map((hole) => this.polyline2(orientRing(hole, false), true)));
89
+ }
90
+ rectangleProfile(xDim, yDim, center = [0, 0]) {
91
+ const position = this.step.add('IFCAXIS2PLACEMENT2D', this.point2(center), null);
92
+ return this.step.add('IFCRECTANGLEPROFILEDEF', enumValue('AREA'), null, position, xDim, yDim);
93
+ }
94
+ circleProfile(radius) {
95
+ const position = this.step.add('IFCAXIS2PLACEMENT2D', this.point2([0, 0]), null);
96
+ return this.step.add('IFCCIRCLEPROFILEDEF', enumValue('AREA'), null, position, radius);
97
+ }
98
+ /** Vertical prism of `profile`, from local z = `bottom` upwards by `depth`. */
99
+ extrusion(profile, bottom, depth, color) {
100
+ const solid = this.step.add('IFCEXTRUDEDAREASOLID', profile, this.axis2Placement3D({ origin: [0, 0, bottom], angle: 0 }), this.direction([0, 0, 1]), depth);
101
+ if (color)
102
+ this.styleItem(solid, color);
103
+ return solid;
104
+ }
105
+ triangulatedFaceSet(set) {
106
+ const { positions, indices } = set;
107
+ const vertexCount = Math.floor(positions.length / 3);
108
+ if (vertexCount < 3)
109
+ return null;
110
+ const triangles = [];
111
+ const indexCount = indices.length > 0 ? indices.length : vertexCount;
112
+ for (let i = 0; i + 2 < indexCount; i += 3) {
113
+ const a = indices.length > 0 ? indices[i] : i;
114
+ const b = indices.length > 0 ? indices[i + 1] : i + 1;
115
+ const c = indices.length > 0 ? indices[i + 2] : i + 2;
116
+ if (a === b || b === c || a === c)
117
+ continue;
118
+ if (a >= vertexCount || b >= vertexCount || c >= vertexCount)
119
+ continue;
120
+ triangles.push(`(${a + 1},${b + 1},${c + 1})`);
121
+ }
122
+ if (triangles.length === 0)
123
+ return null;
124
+ const coords = [];
125
+ for (let i = 0; i < vertexCount; i++) {
126
+ coords.push(`(${formatReal(positions[i * 3], MESH_DECIMALS)},${formatReal(positions[i * 3 + 1], MESH_DECIMALS)},${formatReal(positions[i * 3 + 2], MESH_DECIMALS)})`);
127
+ }
128
+ const pointList = this.step.add('IFCCARTESIANPOINTLIST3D', raw(`(${coords.join(',')})`));
129
+ const faceSet = this.step.add('IFCTRIANGULATEDFACESET', pointList, null, null, raw(`(${triangles.join(',')})`), null);
130
+ if (set.color)
131
+ this.styleItem(faceSet, set.color);
132
+ return faceSet;
133
+ }
134
+ surfaceStyle(color) {
135
+ const [r, g, b] = color.rgb.map((c) => Math.round(Math.max(0, Math.min(1, c)) * 1000) / 1000);
136
+ const transparency = Math.round((1 - Math.max(0, Math.min(1, color.opacity))) * 1000) / 1000;
137
+ const key = `${r},${g},${b},${transparency}`;
138
+ const cached = this.styleCache.get(key);
139
+ if (cached)
140
+ return cached;
141
+ const rgb = this.step.add('IFCCOLOURRGB', null, r, g, b);
142
+ const shading = this.step.add('IFCSURFACESTYLESHADING', rgb, transparency);
143
+ const style = this.step.add('IFCSURFACESTYLE', null, enumValue('BOTH'), [shading]);
144
+ this.styleCache.set(key, style);
145
+ return style;
146
+ }
147
+ styleItem(item, color) {
148
+ this.step.add('IFCSTYLEDITEM', item, [this.surfaceStyle(color)], null);
149
+ }
150
+ shape(representations) {
151
+ return this.step.add('IFCPRODUCTDEFINITIONSHAPE', null, null, representations);
152
+ }
153
+ bodyRepresentation(type, items) {
154
+ return this.step.add('IFCSHAPEREPRESENTATION', this.bodyContext, 'Body', type, items);
155
+ }
156
+ axisRepresentation(curve) {
157
+ return this.step.add('IFCSHAPEREPRESENTATION', this.axisContext, 'Axis', 'Curve2D', [curve]);
158
+ }
159
+ rel(type, seed, ...args) {
160
+ return this.step.add(type, this.guid(seed), this.ownerHistory, null, null, ...args);
161
+ }
162
+ propertySet(seed, name, properties, relatedObjects) {
163
+ const props = properties
164
+ .filter(([, value]) => value !== undefined && value !== null)
165
+ .map(([propName, value]) => this.step.add('IFCPROPERTYSINGLEVALUE', propName, null, value, null));
166
+ if (props.length === 0 || relatedObjects.length === 0)
167
+ return;
168
+ const pset = this.step.add('IFCPROPERTYSET', this.guid(`${seed}:pset:${name}`), this.ownerHistory, name, null, props);
169
+ this.rel('IFCRELDEFINESBYPROPERTIES', `${seed}:rel:${name}`, relatedObjects, pset);
170
+ }
171
+ lengthQuantities(seed, name, quantities, relatedObjects) {
172
+ const refs = quantities.map(([qName, value]) => this.step.add('IFCQUANTITYLENGTH', qName, null, null, value, null));
173
+ const set = this.step.add('IFCELEMENTQUANTITY', this.guid(`${seed}:qto:${name}`), this.ownerHistory, name, null, null, refs);
174
+ this.rel('IFCRELDEFINESBYPROPERTIES', `${seed}:rel:${name}`, relatedObjects, set);
175
+ }
176
+ }
177
+ export const label = (value) => typed('IFCLABEL', value);
178
+ export const identifier = (value) => typed('IFCIDENTIFIER', value);
179
+ export const text = (value) => typed('IFCTEXT', value);
180
+ export const bool = (value) => typed('IFCBOOLEAN', value);
181
+ export const length = (value) => typed('IFCLENGTHMEASURE', value);
182
+ export const ratio = (value) => typed('IFCPOSITIVERATIOMEASURE', value);
183
+ export { DERIVED, enumValue, int };
@@ -0,0 +1,3 @@
1
+ export { ifcGuidFromSeed, isIfcGuid } from './guid';
2
+ export { buildIfcExport, exportSceneToIfc, type IfcExportInput, type IfcExportResult, type IfcExportSkip, type IfcExportSummary, type IfcMeshPart, } from './scene-to-ifc';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/export/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AACnD,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,WAAW,GACjB,MAAM,gBAAgB,CAAA"}
@@ -0,0 +1,2 @@
1
+ export { ifcGuidFromSeed, isIfcGuid } from './guid';
2
+ export { buildIfcExport, exportSceneToIfc, } from './scene-to-ifc';
@@ -0,0 +1,45 @@
1
+ import { type AnyNode, type Collection } from '@pascal-app/core';
2
+ /** Triangle geometry for one node, in the rendered scene's world frame (metres, Y-up). */
3
+ export interface IfcMeshPart {
4
+ positions: ArrayLike<number>;
5
+ indices?: ArrayLike<number>;
6
+ /** sRGB components in 0..1. */
7
+ color?: [number, number, number];
8
+ opacity?: number;
9
+ /** Semantic subpart supplied by the renderer. */
10
+ role?: string;
11
+ }
12
+ export interface IfcExportInput {
13
+ nodes: Record<string, AnyNode>;
14
+ collections?: Readonly<Record<string, Collection>>;
15
+ /** Rendered geometry keyed by node id; required for everything that is not a native IFC element. */
16
+ meshes?: ReadonlyMap<string, IfcMeshPart[]>;
17
+ projectName?: string;
18
+ author?: string;
19
+ organization?: string;
20
+ /** Header and owner-history time; defaults to now. */
21
+ timestamp?: Date;
22
+ /** Skip nodes hidden in the editor (`visible: false` on the node or an ancestor). */
23
+ onlyVisible?: boolean;
24
+ /** Node kinds left out entirely, with their descendants. */
25
+ excludedNodeTypes?: readonly string[];
26
+ }
27
+ export interface IfcExportSkip {
28
+ nodeId: string;
29
+ type: string;
30
+ reason: 'no-geometry' | 'degenerate' | 'no-level';
31
+ }
32
+ export interface IfcExportSummary {
33
+ /** Exported building elements and spaces, by IFC class. */
34
+ elements: Record<string, number>;
35
+ skipped: IfcExportSkip[];
36
+ }
37
+ export interface IfcExportResult {
38
+ ifc: string;
39
+ summary: IfcExportSummary;
40
+ }
41
+ /** Serialize a Pascal scene graph as an IFC4 STEP file plus an export report. */
42
+ export declare function buildIfcExport(input: IfcExportInput): IfcExportResult;
43
+ /** Serialize a Pascal scene graph as IFC4 STEP text. */
44
+ export declare function exportSceneToIfc(input: IfcExportInput): string;
45
+ //# sourceMappingURL=scene-to-ifc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-to-ifc.d.ts","sourceRoot":"","sources":["../../src/export/scene-to-ifc.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EAIZ,KAAK,UAAU,EAmChB,MAAM,kBAAkB,CAAA;AAwBzB,0FAA0F;AAC1F,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,SAAS,CAAC,MAAM,CAAC,CAAA;IAC5B,OAAO,CAAC,EAAE,SAAS,CAAC,MAAM,CAAC,CAAA;IAC3B,+BAA+B;IAC/B,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,iDAAiD;IACjD,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAA;IAClD,oGAAoG;IACpG,MAAM,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAA;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,sDAAsD;IACtD,SAAS,CAAC,EAAE,IAAI,CAAA;IAChB,qFAAqF;IACrF,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,4DAA4D;IAC5D,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CACtC;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,aAAa,GAAG,YAAY,GAAG,UAAU,CAAA;CAClD;AAED,MAAM,WAAW,gBAAgB;IAC/B,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,OAAO,EAAE,aAAa,EAAE,CAAA;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,gBAAgB,CAAA;CAC1B;AAoMD,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe,CAkyCrE;AAiCD,wDAAwD;AACxD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAE9D"}