@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
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal ISO-10303-21 (STEP physical file) writer for IFC4.
|
|
3
|
+
*
|
|
4
|
+
* Entities are serialized the moment they are added, so the file order is
|
|
5
|
+
* the insertion order and entity numbers are deterministic for a given
|
|
6
|
+
* input. Attribute values are plain TS values; the wrappers below cover the
|
|
7
|
+
* STEP-specific literals (enums, integers, typed selects, `$` and `*`).
|
|
8
|
+
*/
|
|
9
|
+
export declare class StepRef {
|
|
10
|
+
readonly id: number;
|
|
11
|
+
constructor(id: number);
|
|
12
|
+
}
|
|
13
|
+
declare class StepEnum {
|
|
14
|
+
readonly value: string;
|
|
15
|
+
constructor(value: string);
|
|
16
|
+
}
|
|
17
|
+
declare class StepInt {
|
|
18
|
+
readonly value: number;
|
|
19
|
+
constructor(value: number);
|
|
20
|
+
}
|
|
21
|
+
declare class StepTyped {
|
|
22
|
+
readonly type: string;
|
|
23
|
+
readonly value: StepValue;
|
|
24
|
+
constructor(type: string, value: StepValue);
|
|
25
|
+
}
|
|
26
|
+
declare class StepRaw {
|
|
27
|
+
readonly text: string;
|
|
28
|
+
constructor(text: string);
|
|
29
|
+
}
|
|
30
|
+
declare const DERIVED_TOKEN: unique symbol;
|
|
31
|
+
export type StepValue = null | undefined | boolean | number | string | StepRef | StepEnum | StepInt | StepTyped | StepRaw | typeof DERIVED_TOKEN | readonly StepValue[];
|
|
32
|
+
/** `*` — attribute re-declared as derived in a subtype. */
|
|
33
|
+
export declare const DERIVED: typeof DERIVED_TOKEN;
|
|
34
|
+
export declare const enumValue: (value: string) => StepEnum;
|
|
35
|
+
export declare const int: (value: number) => StepInt;
|
|
36
|
+
export declare const typed: (type: string, value: StepValue) => StepTyped;
|
|
37
|
+
/** Pre-serialized attribute text, for large aggregates built in a tight loop. */
|
|
38
|
+
export declare const raw: (text: string) => StepRaw;
|
|
39
|
+
export declare function formatReal(value: number, decimals?: number): string;
|
|
40
|
+
/** STEP string literal with ISO 10303-21 escapes for quotes, backslashes and non-ASCII. */
|
|
41
|
+
export declare function formatString(value: string): string;
|
|
42
|
+
export declare function formatValue(value: StepValue): string;
|
|
43
|
+
export interface StepHeader {
|
|
44
|
+
description: string;
|
|
45
|
+
fileName: string;
|
|
46
|
+
timestamp: string;
|
|
47
|
+
author: string;
|
|
48
|
+
organization: string;
|
|
49
|
+
preprocessor: string;
|
|
50
|
+
originatingSystem: string;
|
|
51
|
+
schema: string;
|
|
52
|
+
}
|
|
53
|
+
export declare class StepWriter {
|
|
54
|
+
private readonly lines;
|
|
55
|
+
private readonly shared;
|
|
56
|
+
private nextId;
|
|
57
|
+
readonly counts: Map<string, number>;
|
|
58
|
+
add(type: string, ...args: StepValue[]): StepRef;
|
|
59
|
+
/** Reuse one entity for identical simple values (points, directions, styles). */
|
|
60
|
+
addShared(type: string, ...args: StepValue[]): StepRef;
|
|
61
|
+
serialize(header: StepHeader): string;
|
|
62
|
+
}
|
|
63
|
+
export {};
|
|
64
|
+
//# sourceMappingURL=step.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"step.d.ts","sourceRoot":"","sources":["../../src/export/step.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,qBAAa,OAAO;IACN,QAAQ,CAAC,EAAE,EAAE,MAAM;gBAAV,EAAE,EAAE,MAAM;CAChC;AAED,cAAM,QAAQ;IACA,QAAQ,CAAC,KAAK,EAAE,MAAM;gBAAb,KAAK,EAAE,MAAM;CACnC;AAED,cAAM,OAAO;IACC,QAAQ,CAAC,KAAK,EAAE,MAAM;gBAAb,KAAK,EAAE,MAAM;CACnC;AAED,cAAM,SAAS;IAEX,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,KAAK,EAAE,SAAS;gBADhB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS;CAE5B;AAED,cAAM,OAAO;IACC,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM;CAClC;AAED,QAAA,MAAM,aAAa,eAAoB,CAAA;AAEvC,MAAM,MAAM,SAAS,GACjB,IAAI,GACJ,SAAS,GACT,OAAO,GACP,MAAM,GACN,MAAM,GACN,OAAO,GACP,QAAQ,GACR,OAAO,GACP,SAAS,GACT,OAAO,GACP,OAAO,aAAa,GACpB,SAAS,SAAS,EAAE,CAAA;AAExB,2DAA2D;AAC3D,eAAO,MAAM,OAAO,EAAE,OAAO,aAA6B,CAAA;AAC1D,eAAO,MAAM,SAAS,GAAI,OAAO,MAAM,aAAwB,CAAA;AAC/D,eAAO,MAAM,GAAG,GAAI,OAAO,MAAM,YAAmC,CAAA;AACpE,eAAO,MAAM,KAAK,GAAI,MAAM,MAAM,EAAE,OAAO,SAAS,cAA+B,CAAA;AACnF,iFAAiF;AACjF,eAAO,MAAM,GAAG,GAAI,MAAM,MAAM,YAAsB,CAAA;AAEtD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,SAAI,GAAG,MAAM,CAc9D;AAED,2FAA2F;AAC3F,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAsBlD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAYpD;AAED,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,iBAAiB,EAAE,MAAM,CAAA;IACzB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA6B;IACpD,OAAO,CAAC,MAAM,CAAI;IAClB,QAAQ,CAAC,MAAM,sBAA4B;IAE3C,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,GAAG,OAAO;IAOhD,iFAAiF;IACjF,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,GAAG,OAAO;IAStD,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM;CAoBtC"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal ISO-10303-21 (STEP physical file) writer for IFC4.
|
|
3
|
+
*
|
|
4
|
+
* Entities are serialized the moment they are added, so the file order is
|
|
5
|
+
* the insertion order and entity numbers are deterministic for a given
|
|
6
|
+
* input. Attribute values are plain TS values; the wrappers below cover the
|
|
7
|
+
* STEP-specific literals (enums, integers, typed selects, `$` and `*`).
|
|
8
|
+
*/
|
|
9
|
+
export class StepRef {
|
|
10
|
+
id;
|
|
11
|
+
constructor(id) {
|
|
12
|
+
this.id = id;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
class StepEnum {
|
|
16
|
+
value;
|
|
17
|
+
constructor(value) {
|
|
18
|
+
this.value = value;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
class StepInt {
|
|
22
|
+
value;
|
|
23
|
+
constructor(value) {
|
|
24
|
+
this.value = value;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
class StepTyped {
|
|
28
|
+
type;
|
|
29
|
+
value;
|
|
30
|
+
constructor(type, value) {
|
|
31
|
+
this.type = type;
|
|
32
|
+
this.value = value;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
class StepRaw {
|
|
36
|
+
text;
|
|
37
|
+
constructor(text) {
|
|
38
|
+
this.text = text;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const DERIVED_TOKEN = Symbol('derived');
|
|
42
|
+
/** `*` — attribute re-declared as derived in a subtype. */
|
|
43
|
+
export const DERIVED = DERIVED_TOKEN;
|
|
44
|
+
export const enumValue = (value) => new StepEnum(value);
|
|
45
|
+
export const int = (value) => new StepInt(Math.round(value));
|
|
46
|
+
export const typed = (type, value) => new StepTyped(type, value);
|
|
47
|
+
/** Pre-serialized attribute text, for large aggregates built in a tight loop. */
|
|
48
|
+
export const raw = (text) => new StepRaw(text);
|
|
49
|
+
export function formatReal(value, decimals = 9) {
|
|
50
|
+
if (!Number.isFinite(value))
|
|
51
|
+
return '0.';
|
|
52
|
+
const scale = 10 ** decimals;
|
|
53
|
+
let rounded = Math.round(value * scale) / scale;
|
|
54
|
+
if (Object.is(rounded, -0))
|
|
55
|
+
rounded = 0;
|
|
56
|
+
let text = String(rounded);
|
|
57
|
+
const exponent = text.indexOf('e');
|
|
58
|
+
if (exponent >= 0) {
|
|
59
|
+
const mantissa = text.slice(0, exponent);
|
|
60
|
+
text = `${mantissa.includes('.') ? mantissa : `${mantissa}.`}E${text.slice(exponent + 1)}`;
|
|
61
|
+
}
|
|
62
|
+
else if (!text.includes('.')) {
|
|
63
|
+
text += '.';
|
|
64
|
+
}
|
|
65
|
+
return text;
|
|
66
|
+
}
|
|
67
|
+
/** STEP string literal with ISO 10303-21 escapes for quotes, backslashes and non-ASCII. */
|
|
68
|
+
export function formatString(value) {
|
|
69
|
+
let out = "'";
|
|
70
|
+
let wide = '';
|
|
71
|
+
const flushWide = () => {
|
|
72
|
+
if (!wide)
|
|
73
|
+
return;
|
|
74
|
+
out += `\\X2\\${wide}\\X0\\`;
|
|
75
|
+
wide = '';
|
|
76
|
+
};
|
|
77
|
+
for (const char of value) {
|
|
78
|
+
const code = char.codePointAt(0);
|
|
79
|
+
if (code >= 0x20 && code <= 0x7e) {
|
|
80
|
+
flushWide();
|
|
81
|
+
out += char === "'" ? "''" : char === '\\' ? '\\\\' : char;
|
|
82
|
+
}
|
|
83
|
+
else if (code > 0xffff) {
|
|
84
|
+
flushWide();
|
|
85
|
+
out += `\\X4\\${code.toString(16).toUpperCase().padStart(8, '0')}\\X0\\`;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
wide += code.toString(16).toUpperCase().padStart(4, '0');
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
flushWide();
|
|
92
|
+
return `${out}'`;
|
|
93
|
+
}
|
|
94
|
+
export function formatValue(value) {
|
|
95
|
+
if (value === null || value === undefined)
|
|
96
|
+
return '$';
|
|
97
|
+
if (value === DERIVED_TOKEN)
|
|
98
|
+
return '*';
|
|
99
|
+
if (typeof value === 'boolean')
|
|
100
|
+
return value ? '.T.' : '.F.';
|
|
101
|
+
if (typeof value === 'number')
|
|
102
|
+
return formatReal(value);
|
|
103
|
+
if (typeof value === 'string')
|
|
104
|
+
return formatString(value);
|
|
105
|
+
if (value instanceof StepRef)
|
|
106
|
+
return `#${value.id}`;
|
|
107
|
+
if (value instanceof StepEnum)
|
|
108
|
+
return `.${value.value}.`;
|
|
109
|
+
if (value instanceof StepInt)
|
|
110
|
+
return String(value.value);
|
|
111
|
+
if (value instanceof StepRaw)
|
|
112
|
+
return value.text;
|
|
113
|
+
if (value instanceof StepTyped)
|
|
114
|
+
return `${value.type}(${formatValue(value.value)})`;
|
|
115
|
+
return `(${value.map(formatValue).join(',')})`;
|
|
116
|
+
}
|
|
117
|
+
export class StepWriter {
|
|
118
|
+
lines = [];
|
|
119
|
+
shared = new Map();
|
|
120
|
+
nextId = 1;
|
|
121
|
+
counts = new Map();
|
|
122
|
+
add(type, ...args) {
|
|
123
|
+
const ref = new StepRef(this.nextId++);
|
|
124
|
+
this.lines.push(`#${ref.id}=${type}(${args.map(formatValue).join(',')});`);
|
|
125
|
+
this.counts.set(type, (this.counts.get(type) ?? 0) + 1);
|
|
126
|
+
return ref;
|
|
127
|
+
}
|
|
128
|
+
/** Reuse one entity for identical simple values (points, directions, styles). */
|
|
129
|
+
addShared(type, ...args) {
|
|
130
|
+
const key = `${type}(${args.map(formatValue).join(',')})`;
|
|
131
|
+
const existing = this.shared.get(key);
|
|
132
|
+
if (existing)
|
|
133
|
+
return existing;
|
|
134
|
+
const ref = this.add(type, ...args);
|
|
135
|
+
this.shared.set(key, ref);
|
|
136
|
+
return ref;
|
|
137
|
+
}
|
|
138
|
+
serialize(header) {
|
|
139
|
+
const head = [
|
|
140
|
+
'ISO-10303-21;',
|
|
141
|
+
'HEADER;',
|
|
142
|
+
`FILE_DESCRIPTION((${formatString(header.description)}),'2;1');`,
|
|
143
|
+
`FILE_NAME(${[
|
|
144
|
+
formatString(header.fileName),
|
|
145
|
+
formatString(header.timestamp),
|
|
146
|
+
`(${formatString(header.author)})`,
|
|
147
|
+
`(${formatString(header.organization)})`,
|
|
148
|
+
formatString(header.preprocessor),
|
|
149
|
+
formatString(header.originatingSystem),
|
|
150
|
+
"''",
|
|
151
|
+
].join(',')});`,
|
|
152
|
+
`FILE_SCHEMA((${formatString(header.schema)}));`,
|
|
153
|
+
'ENDSEC;',
|
|
154
|
+
'DATA;',
|
|
155
|
+
];
|
|
156
|
+
return `${[...head, ...this.lines, 'ENDSEC;', 'END-ISO-10303-21;'].join('\n')}\n`;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type WallNode, type wallSupportForNodes } from '@pascal-app/core';
|
|
2
|
+
type WallSupport = ReturnType<typeof wallSupportForNodes>;
|
|
3
|
+
/** A run of the wall (normalised arc length) on one side, with its bottom elevation. */
|
|
4
|
+
export interface WallBaseCell {
|
|
5
|
+
t0: number;
|
|
6
|
+
t1: number;
|
|
7
|
+
/** Which half of the thickness: both, or the `a` / `b` face half. */
|
|
8
|
+
side: 'both' | 'a' | 'b';
|
|
9
|
+
bottom: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The wall renderer's stepped bottom (`generateExtrudedWall`), as additive
|
|
13
|
+
* cells: it extrudes from the lowest exposed support, then cuts away the
|
|
14
|
+
* volume below every higher-supported run — per face when the two faces sit
|
|
15
|
+
* on different floors. Returned cells cover the whole wall.
|
|
16
|
+
*/
|
|
17
|
+
export declare function wallBaseCells(wall: WallNode, support: WallSupport, top: number): WallBaseCell[];
|
|
18
|
+
/**
|
|
19
|
+
* Plan band (Pascal x/z) covering a cell's run and side, generously past the
|
|
20
|
+
* faces and past the wall ends so mitred corners are included — the same
|
|
21
|
+
* band the renderer cuts with.
|
|
22
|
+
*/
|
|
23
|
+
export declare function wallCellBand(wall: WallNode, cell: WallBaseCell): [number, number][];
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=wall-base.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wall-base.d.ts","sourceRoot":"","sources":["../../src/export/wall-base.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,QAAQ,EACb,KAAK,mBAAmB,EACzB,MAAM,kBAAkB,CAAA;AAEzB,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAGzD,wFAAwF;AACxF,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;IACV,qEAAqE;IACrE,IAAI,EAAE,MAAM,GAAG,GAAG,GAAG,GAAG,CAAA;IACxB,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,EAAE,CAwE/F;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAmBnF"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { DEFAULT_WALL_THICKNESS, getWallBodyCenterOffset, getWallCurveFrameAt, isCurvedWall, resolveWallFaceBottom, } from '@pascal-app/core';
|
|
2
|
+
/**
|
|
3
|
+
* The wall renderer's stepped bottom (`generateExtrudedWall`), as additive
|
|
4
|
+
* cells: it extrudes from the lowest exposed support, then cuts away the
|
|
5
|
+
* volume below every higher-supported run — per face when the two faces sit
|
|
6
|
+
* on different floors. Returned cells cover the whole wall.
|
|
7
|
+
*/
|
|
8
|
+
export function wallBaseCells(wall, support, top) {
|
|
9
|
+
const slabElevation = support.elevation;
|
|
10
|
+
const baseElevation = support.baseElevation ?? slabElevation;
|
|
11
|
+
const baseSegments = support.baseSegments?.length
|
|
12
|
+
? support.baseSegments
|
|
13
|
+
: [{ start: 0, end: 1, elevation: baseElevation }];
|
|
14
|
+
const faceBase = support.faceDatum
|
|
15
|
+
? {
|
|
16
|
+
a: resolveWallFaceBottom(support.faceDatum.a, baseSegments, slabElevation),
|
|
17
|
+
b: resolveWallFaceBottom(support.faceDatum.b, baseSegments, slabElevation),
|
|
18
|
+
}
|
|
19
|
+
: undefined;
|
|
20
|
+
const useFaceBase = faceBase !== undefined &&
|
|
21
|
+
![faceBase.a, faceBase.b].every((segments) => JSON.stringify(segments) === JSON.stringify(baseSegments));
|
|
22
|
+
const lowest = useFaceBase
|
|
23
|
+
? Math.min(...[...faceBase.a, ...faceBase.b].flatMap((segment) => [
|
|
24
|
+
segment.elevation,
|
|
25
|
+
segment.endElevation ?? segment.elevation,
|
|
26
|
+
]), slabElevation)
|
|
27
|
+
: Math.min(baseElevation, slabElevation);
|
|
28
|
+
const profiles = useFaceBase
|
|
29
|
+
? [
|
|
30
|
+
...faceBase.a.map((segment) => ({ segment, face: 'a' })),
|
|
31
|
+
...faceBase.b.map((segment) => ({ segment, face: 'b' })),
|
|
32
|
+
]
|
|
33
|
+
: baseSegments.map((segment) => ({ segment, face: undefined }));
|
|
34
|
+
const raised = profiles.flatMap(({ segment, face }) => {
|
|
35
|
+
const elevation = face
|
|
36
|
+
? Math.min(Math.max(segment.elevation, segment.endElevation ?? segment.elevation), top)
|
|
37
|
+
: Math.min(segment.elevation, slabElevation);
|
|
38
|
+
const t0 = Math.max(0, Math.min(1, segment.start));
|
|
39
|
+
const t1 = Math.max(0, Math.min(1, segment.end));
|
|
40
|
+
return elevation - lowest > 1e-6 && t1 - t0 > 1e-7 ? [{ t0, t1, face, elevation }] : [];
|
|
41
|
+
});
|
|
42
|
+
if (raised.length === 0)
|
|
43
|
+
return [{ t0: 0, t1: 1, side: 'both', bottom: lowest }];
|
|
44
|
+
const cuts = [...new Set([0, 1, ...raised.flatMap((run) => [run.t0, run.t1])])].sort((a, b) => a - b);
|
|
45
|
+
const sides = useFaceBase ? ['a', 'b'] : ['both'];
|
|
46
|
+
const cells = [];
|
|
47
|
+
for (const side of sides) {
|
|
48
|
+
for (let i = 1; i < cuts.length; i++) {
|
|
49
|
+
const t0 = cuts[i - 1];
|
|
50
|
+
const t1 = cuts[i];
|
|
51
|
+
if (t1 - t0 <= 1e-9)
|
|
52
|
+
continue;
|
|
53
|
+
const mid = (t0 + t1) / 2;
|
|
54
|
+
const bottom = Math.max(lowest, ...raised
|
|
55
|
+
.filter((run) => run.t0 <= mid && run.t1 >= mid && (run.face === undefined || run.face === side))
|
|
56
|
+
.map((run) => run.elevation));
|
|
57
|
+
const previous = cells[cells.length - 1];
|
|
58
|
+
if (previous && previous.side === side && previous.t1 === t0 && previous.bottom === bottom) {
|
|
59
|
+
previous.t1 = t1;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
cells.push({ t0, t1, side, bottom });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return cells;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Plan band (Pascal x/z) covering a cell's run and side, generously past the
|
|
70
|
+
* faces and past the wall ends so mitred corners are included — the same
|
|
71
|
+
* band the renderer cuts with.
|
|
72
|
+
*/
|
|
73
|
+
export function wallCellBand(wall, cell) {
|
|
74
|
+
const reach = Math.max((wall.thickness ?? DEFAULT_WALL_THICKNESS) * 2, 0.2);
|
|
75
|
+
const center = getWallBodyCenterOffset(wall);
|
|
76
|
+
const left = cell.side === 'b' ? center : reach;
|
|
77
|
+
const right = cell.side === 'a' ? center : -reach;
|
|
78
|
+
const samples = isCurvedWall(wall) ? Math.max(2, Math.ceil((cell.t1 - cell.t0) * 24)) : 1;
|
|
79
|
+
const leftSide = [];
|
|
80
|
+
const rightSide = [];
|
|
81
|
+
for (let index = 0; index <= samples; index++) {
|
|
82
|
+
const t = cell.t0 + ((cell.t1 - cell.t0) * index) / samples;
|
|
83
|
+
const frame = getWallCurveFrameAt(wall, t);
|
|
84
|
+
const extension = index === 0 && cell.t0 <= 1e-7 ? -reach : index === samples && cell.t1 >= 1 - 1e-7 ? reach : 0;
|
|
85
|
+
const x = frame.point.x + frame.tangent.x * extension;
|
|
86
|
+
const z = frame.point.y + frame.tangent.y * extension;
|
|
87
|
+
leftSide.push([x + frame.normal.x * left, z + frame.normal.y * left]);
|
|
88
|
+
rightSide.push([x + frame.normal.x * right, z + frame.normal.y * right]);
|
|
89
|
+
}
|
|
90
|
+
return [...leftSide, ...rightSide.reverse()];
|
|
91
|
+
}
|
package/dist/ids.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../src/ids.ts"],"names":[],"mappings":"AA0BA,wBAAgB,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAIhD;AAED,wBAAgB,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,GAAG,CAAC,IAAI,MAAM,EAAE,CAMpE"}
|
package/dist/ids.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Node ids are drawn from a sequence seeded by the IFC bytes, not from a random
|
|
2
|
+
// source: room detection breaks near-ties by id order, so random ids made the
|
|
3
|
+
// same file import differently from run to run.
|
|
4
|
+
let seedA = 0;
|
|
5
|
+
let seedB = 0;
|
|
6
|
+
let counter = 0;
|
|
7
|
+
function fnv1a(bytes, basis) {
|
|
8
|
+
let hash = basis;
|
|
9
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
10
|
+
hash ^= bytes[i];
|
|
11
|
+
hash = Math.imul(hash, 0x01000193);
|
|
12
|
+
}
|
|
13
|
+
return hash >>> 0;
|
|
14
|
+
}
|
|
15
|
+
function mix(value) {
|
|
16
|
+
let x = value >>> 0;
|
|
17
|
+
x ^= x >>> 16;
|
|
18
|
+
x = Math.imul(x, 0x7feb352d);
|
|
19
|
+
x ^= x >>> 15;
|
|
20
|
+
x = Math.imul(x, 0x846ca68b);
|
|
21
|
+
x ^= x >>> 16;
|
|
22
|
+
return x >>> 0;
|
|
23
|
+
}
|
|
24
|
+
export function seedIds(source) {
|
|
25
|
+
seedA = fnv1a(source, 0x811c9dc5);
|
|
26
|
+
seedB = fnv1a(source, 0x9e3779b9);
|
|
27
|
+
counter = 0;
|
|
28
|
+
}
|
|
29
|
+
export function nextId(prefix) {
|
|
30
|
+
counter++;
|
|
31
|
+
const high = mix(seedA ^ Math.imul(counter, 0x9e3779b1));
|
|
32
|
+
const low = mix(seedB + Math.imul(counter, 0x85ebca6b));
|
|
33
|
+
const body = `${high.toString(36).padStart(7, '0')}${low.toString(36).padStart(7, '0')}`;
|
|
34
|
+
return `${prefix}_${body}`;
|
|
35
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { type AnyNode, type AnyNodeId } from '@pascal-app/core';
|
|
1
|
+
import { type AnyNode, type AnyNodeId, type Collection } from '@pascal-app/core';
|
|
2
2
|
import { type IfcConversionSimplificationOptions } from './cleanup';
|
|
3
3
|
export type { IfcConversionSimplificationOptions, IfcConversionSimplificationStats, } from './cleanup';
|
|
4
4
|
export type PascalNode = AnyNode;
|
|
5
5
|
export interface PascalSceneGraph {
|
|
6
6
|
nodes: Record<AnyNodeId, AnyNode>;
|
|
7
7
|
rootNodeIds: AnyNodeId[];
|
|
8
|
-
collections?: Record<string,
|
|
8
|
+
collections?: Record<string, Collection>;
|
|
9
9
|
}
|
|
10
10
|
export interface ConversionOptions {
|
|
11
11
|
swapYZ?: boolean;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,SAAS,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,SAAS,EAId,KAAK,UAAU,EAmBhB,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EAAE,KAAK,kCAAkC,EAA+B,MAAM,WAAW,CAAA;AAQhG,YAAY,EACV,kCAAkC,EAClC,gCAAgC,GACjC,MAAM,WAAW,CAAA;AAElB,MAAM,MAAM,UAAU,GAAG,OAAO,CAAA;AAEhC,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;IACjC,WAAW,EAAE,SAAS,EAAE,CAAA;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;CACzC;AAiyBD,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,sBAAsB,CAAC,EAAE,OAAO,CAAA;IAChC,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,OAAO,GAAG,kCAAkC,CAAA;IACvD,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAa7D,CAAA;AAED,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,UAAU,EACnB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,EACvD,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,gBAAgB,CAAC,CA8/D3B"}
|