@pascal-app/ifc-converter 1.0.0-beta.5 → 1.0.1
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/LICENSE +21 -0
- package/README.md +25 -0
- package/dist/beam-geometry.d.ts +14 -0
- package/dist/beam-geometry.d.ts.map +1 -0
- package/dist/beam-geometry.js +92 -0
- package/dist/cleanup.d.ts.map +1 -1
- package/dist/cleanup.js +24 -1
- package/dist/door-semantics.d.ts +8 -0
- package/dist/door-semantics.d.ts.map +1 -0
- package/dist/door-semantics.js +56 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +578 -222
- package/dist/storey-semantics.d.ts +6 -0
- package/dist/storey-semantics.d.ts.map +1 -0
- package/dist/storey-semantics.js +5 -0
- package/package.json +2 -2
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pascal Group Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -5,3 +5,28 @@ IFC bytes, returns `{ nodes, rootNodeIds, stats }` shaped against
|
|
|
5
5
|
`@pascal-app/core` schemas.
|
|
6
6
|
|
|
7
7
|
No DOM, no React. The UI lives in `apps/ifc-converter`.
|
|
8
|
+
|
|
9
|
+
`IFCBEAM` and `IFCBEAMSTANDARDCASE` are imported as editable `block` nodes.
|
|
10
|
+
Their triangulated IFC geometry preserves cross-sections, rotations, and slopes;
|
|
11
|
+
positions are relative to the containing storey. IFC IDs, names, properties,
|
|
12
|
+
and material metadata are retained. Beams without renderable geometry are
|
|
13
|
+
reported in the conversion log. Imported beams use the block editor rather than
|
|
14
|
+
dedicated parametric beam controls.
|
|
15
|
+
|
|
16
|
+
Native Pascal nodes are produced when the converter can recover the required
|
|
17
|
+
parameters for sites, buildings, levels, walls, doors, windows, slabs, columns,
|
|
18
|
+
and IFC spaces (as room zones). IFC stair flights are retained as exact imported
|
|
19
|
+
meshes; roofs retain Pascal hierarchy and source metadata but are not yet a
|
|
20
|
+
complete parametric conversion. Railings, coverings, furnishings, proxies,
|
|
21
|
+
curtain walls, plates, members,
|
|
22
|
+
footings, and elements whose native parameters cannot be recovered are retained
|
|
23
|
+
as selectable `imported-mesh` nodes using their IFC triangle geometry and color.
|
|
24
|
+
Imported meshes are import-only and do not appear as empty objects in the editor
|
|
25
|
+
palette.
|
|
26
|
+
|
|
27
|
+
Door families are derived from `IfcDoor.OperationType`. Glazing is applied from
|
|
28
|
+
the standardized `Pset_DoorCommon.GlazingAreaFraction` property rather than
|
|
29
|
+
from element names or project-specific conventions.
|
|
30
|
+
|
|
31
|
+
Browser callers use the default WebIFC WASM path (`/`). Node callers can pass
|
|
32
|
+
`{ wasmPath: '/absolute/path/to/web-ifc/' }` in `ConversionOptions`.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type BlockTopology } from '@pascal-app/core';
|
|
2
|
+
import type { IfcAPI } from 'web-ifc';
|
|
3
|
+
type Point = [number, number, number];
|
|
4
|
+
export declare function extractBeamGeometry(ifcApi: IfcAPI, modelID: number, expressID: number, options: {
|
|
5
|
+
origin: number[];
|
|
6
|
+
unitFactor: number;
|
|
7
|
+
swapYZ: boolean;
|
|
8
|
+
levelElevation: number;
|
|
9
|
+
}): {
|
|
10
|
+
position: Point;
|
|
11
|
+
topology: BlockTopology;
|
|
12
|
+
} | null;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=beam-geometry.d.ts.map
|
|
@@ -0,0 +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,CAyFrD"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { blockUndirectedEdgeKey } from '@pascal-app/core';
|
|
2
|
+
export function extractBeamGeometry(ifcApi, modelID, expressID, options) {
|
|
3
|
+
const mesh = ifcApi.GetFlatMesh(modelID, expressID);
|
|
4
|
+
const topology = { vertices: [], edges: [], faces: [] };
|
|
5
|
+
const vertexByPosition = new Map();
|
|
6
|
+
const edgeKeys = new Set();
|
|
7
|
+
try {
|
|
8
|
+
for (let g = 0; g < mesh.geometries.size(); g++) {
|
|
9
|
+
const placed = mesh.geometries.get(g);
|
|
10
|
+
const m = placed.flatTransformation;
|
|
11
|
+
const geometry = ifcApi.GetGeometry(modelID, placed.geometryExpressID);
|
|
12
|
+
try {
|
|
13
|
+
const vertices = ifcApi.GetVertexArray(geometry.GetVertexData(), geometry.GetVertexDataSize());
|
|
14
|
+
const indices = ifcApi.GetIndexArray(geometry.GetIndexData(), geometry.GetIndexDataSize());
|
|
15
|
+
const vertexIds = [];
|
|
16
|
+
for (let v = 0; v < vertices.length; v += 6) {
|
|
17
|
+
const x = vertices[v];
|
|
18
|
+
const y = vertices[v + 1];
|
|
19
|
+
const z = vertices[v + 2];
|
|
20
|
+
const wx = m[0] * x + m[4] * y + m[8] * z + m[12];
|
|
21
|
+
const wy = m[1] * x + m[5] * y + m[9] * z + m[13];
|
|
22
|
+
const wz = m[2] * x + m[6] * y + m[10] * z + m[14];
|
|
23
|
+
// web-ifc meshes are already in meters and use (X, Z, -Y).
|
|
24
|
+
// Undo that basis before applying the converter's origin and axis preset.
|
|
25
|
+
const sx = wx - options.origin[0] * options.unitFactor;
|
|
26
|
+
const sy = -wz - options.origin[1] * options.unitFactor;
|
|
27
|
+
const sz = wy - options.origin[2] * options.unitFactor - options.levelElevation;
|
|
28
|
+
const position = options.swapYZ ? [sx, sz, sy] : [sx, sy, sz];
|
|
29
|
+
if (!position.every(Number.isFinite))
|
|
30
|
+
throw new Error('Non-finite beam vertex');
|
|
31
|
+
const key = position.join(',');
|
|
32
|
+
let id = vertexByPosition.get(key);
|
|
33
|
+
if (!id) {
|
|
34
|
+
id = `v${topology.vertices.length}`;
|
|
35
|
+
topology.vertices.push({ id, position });
|
|
36
|
+
vertexByPosition.set(key, id);
|
|
37
|
+
}
|
|
38
|
+
vertexIds.push(id);
|
|
39
|
+
}
|
|
40
|
+
const determinant = m[0] * (m[5] * m[10] - m[9] * m[6]) -
|
|
41
|
+
m[4] * (m[1] * m[10] - m[9] * m[2]) +
|
|
42
|
+
m[8] * (m[1] * m[6] - m[5] * m[2]);
|
|
43
|
+
// Baking a reflection into the vertices also reverses the outward face winding.
|
|
44
|
+
const reverseWinding = determinant < 0 !== options.swapYZ;
|
|
45
|
+
for (let i = 0; i + 2 < indices.length; i += 3) {
|
|
46
|
+
const ids = [vertexIds[indices[i]], vertexIds[indices[i + 1]], vertexIds[indices[i + 2]]];
|
|
47
|
+
if (ids.some((id) => id === undefined))
|
|
48
|
+
throw new Error('Invalid beam triangle index');
|
|
49
|
+
if (new Set(ids).size < 3)
|
|
50
|
+
continue;
|
|
51
|
+
if (reverseWinding)
|
|
52
|
+
ids.reverse();
|
|
53
|
+
topology.faces.push({
|
|
54
|
+
id: `f${topology.faces.length}`,
|
|
55
|
+
vertexIds: ids,
|
|
56
|
+
materialSlot: 'body',
|
|
57
|
+
});
|
|
58
|
+
for (let j = 0; j < 3; j++) {
|
|
59
|
+
const a = ids[j];
|
|
60
|
+
const b = ids[(j + 1) % 3];
|
|
61
|
+
const key = blockUndirectedEdgeKey(a, b);
|
|
62
|
+
if (edgeKeys.has(key))
|
|
63
|
+
continue;
|
|
64
|
+
edgeKeys.add(key);
|
|
65
|
+
topology.edges.push({ id: `e${topology.edges.length}`, vertexIds: [a, b] });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
geometry.delete();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
mesh.delete?.();
|
|
76
|
+
}
|
|
77
|
+
if (topology.faces.length === 0)
|
|
78
|
+
return null;
|
|
79
|
+
const min = [Infinity, Infinity, Infinity];
|
|
80
|
+
const max = [-Infinity, -Infinity, -Infinity];
|
|
81
|
+
for (const vertex of topology.vertices) {
|
|
82
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
83
|
+
min[axis] = Math.min(min[axis], vertex.position[axis]);
|
|
84
|
+
max[axis] = Math.max(max[axis], vertex.position[axis]);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const position = [(min[0] + max[0]) / 2, (min[1] + max[1]) / 2, (min[2] + max[2]) / 2];
|
|
88
|
+
for (const vertex of topology.vertices) {
|
|
89
|
+
vertex.position = vertex.position.map((value, axis) => value - position[axis]);
|
|
90
|
+
}
|
|
91
|
+
return { position, topology };
|
|
92
|
+
}
|
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;AAEzB,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;CACjC,CAAA;
|
|
1
|
+
{"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../src/cleanup.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EAMb,MAAM,kBAAkB,CAAA;AAEzB,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;CACjC,CAAA;AAseD,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,UAAU,EACjB,OAAO,GAAE,kCAAuC,GAC/C,gCAAgC,CAoBlC"}
|
package/dist/cleanup.js
CHANGED
|
@@ -142,11 +142,32 @@ function toWallSegment(wall) {
|
|
|
142
142
|
};
|
|
143
143
|
}
|
|
144
144
|
function wallLineTolerance(a, b) {
|
|
145
|
-
|
|
145
|
+
// Fragments must share essentially the same centerline. A tolerance based on
|
|
146
|
+
// half the wall thickness can collapse adjacent walls whose faces merely meet.
|
|
147
|
+
return Math.max(0.005, Math.min(0.025, Math.max(a.thickness, b.thickness) * 0.1));
|
|
146
148
|
}
|
|
147
149
|
function wallHeightCompatible(a, b) {
|
|
148
150
|
return Math.abs(a.height - b.height) <= WALL_HEIGHT_TOLERANCE;
|
|
149
151
|
}
|
|
152
|
+
function wallMaterialSignature(segment) {
|
|
153
|
+
const metadata = segment.wall.metadata;
|
|
154
|
+
const material = typeof metadata?.material === 'string' ? metadata.material : null;
|
|
155
|
+
const layers = Array.isArray(metadata?.materialLayers)
|
|
156
|
+
? metadata.materialLayers.map((layer) => {
|
|
157
|
+
const value = layer;
|
|
158
|
+
return [
|
|
159
|
+
typeof value.name === 'string' ? value.name : null,
|
|
160
|
+
typeof value.thickness === 'number' ? value.thickness : null,
|
|
161
|
+
];
|
|
162
|
+
})
|
|
163
|
+
: [];
|
|
164
|
+
if (material === null && layers.length === 0)
|
|
165
|
+
return null;
|
|
166
|
+
return JSON.stringify({ material, layers });
|
|
167
|
+
}
|
|
168
|
+
function wallMaterialCompatible(a, b) {
|
|
169
|
+
return wallMaterialSignature(a) === wallMaterialSignature(b);
|
|
170
|
+
}
|
|
150
171
|
function wallIntervalsCompatible(a, b, maxJoinGap) {
|
|
151
172
|
const gap = Math.max(a.t0, b.t0) - Math.min(a.t1, b.t1);
|
|
152
173
|
if (gap <= maxJoinGap)
|
|
@@ -165,6 +186,8 @@ function wallsCanMerge(a, b, maxJoinGap) {
|
|
|
165
186
|
return false;
|
|
166
187
|
if (!wallHeightCompatible(a, b))
|
|
167
188
|
return false;
|
|
189
|
+
if (!wallMaterialCompatible(a, b))
|
|
190
|
+
return false;
|
|
168
191
|
return wallIntervalsCompatible(a, b, maxJoinGap);
|
|
169
192
|
}
|
|
170
193
|
function find(parent, index) {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DoorNode } from '@pascal-app/core';
|
|
2
|
+
type DoorStyle = Partial<DoorNode>;
|
|
3
|
+
/** Map standardized IfcDoor operation values to Pascal door families. */
|
|
4
|
+
export declare function doorStyleFromIfcOperation(operationType: unknown): DoorStyle;
|
|
5
|
+
/** Apply the standardized Pset_DoorCommon glazing fraction after psets load. */
|
|
6
|
+
export declare function doorGlazingStyle(door: DoorNode, glazingAreaFraction: unknown): DoorStyle;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=door-semantics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"door-semantics.d.ts","sourceRoot":"","sources":["../src/door-semantics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAEhD,KAAK,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;AAalC,yEAAyE;AACzE,wBAAgB,yBAAyB,CAAC,aAAa,EAAE,OAAO,GAAG,SAAS,CAoC3E;AAED,gFAAgF;AAChF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,EAAE,mBAAmB,EAAE,OAAO,GAAG,SAAS,CASxF"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const glazedSegments = [
|
|
2
|
+
{
|
|
3
|
+
type: 'glass',
|
|
4
|
+
heightRatio: 1,
|
|
5
|
+
columnRatios: [1],
|
|
6
|
+
dividerThickness: 0.025,
|
|
7
|
+
panelDepth: 0.008,
|
|
8
|
+
panelInset: 0.035,
|
|
9
|
+
},
|
|
10
|
+
];
|
|
11
|
+
/** Map standardized IfcDoor operation values to Pascal door families. */
|
|
12
|
+
export function doorStyleFromIfcOperation(operationType) {
|
|
13
|
+
const operation = String(operationType ?? '').toUpperCase();
|
|
14
|
+
const leafCount = operation.startsWith('DOUBLE_DOOR') ? 2 : 1;
|
|
15
|
+
if (operation.includes('SLIDING')) {
|
|
16
|
+
return {
|
|
17
|
+
doorType: 'sliding',
|
|
18
|
+
leafCount,
|
|
19
|
+
slideDirection: operation.includes('RIGHT') ? 'right' : 'left',
|
|
20
|
+
trackStyle: 'visible',
|
|
21
|
+
threshold: false,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
if (operation.includes('FOLDING')) {
|
|
25
|
+
return {
|
|
26
|
+
doorType: 'folding',
|
|
27
|
+
leafCount,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (operation === 'ROLLINGUP') {
|
|
31
|
+
return {
|
|
32
|
+
doorCategory: 'garage',
|
|
33
|
+
doorType: 'garage-rollup',
|
|
34
|
+
trackStyle: 'overhead',
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (operation.startsWith('DOUBLE_DOOR')) {
|
|
38
|
+
return { doorType: 'double', leafCount: 2 };
|
|
39
|
+
}
|
|
40
|
+
if (operation.includes('SWING_RIGHT'))
|
|
41
|
+
return { hingesSide: 'right' };
|
|
42
|
+
if (operation.includes('SWING_LEFT'))
|
|
43
|
+
return { hingesSide: 'left' };
|
|
44
|
+
return {};
|
|
45
|
+
}
|
|
46
|
+
/** Apply the standardized Pset_DoorCommon glazing fraction after psets load. */
|
|
47
|
+
export function doorGlazingStyle(door, glazingAreaFraction) {
|
|
48
|
+
const fraction = Number(glazingAreaFraction);
|
|
49
|
+
if (!Number.isFinite(fraction) || fraction <= 0)
|
|
50
|
+
return {};
|
|
51
|
+
return {
|
|
52
|
+
doorType: door.doorType === 'double' ? 'french' : door.doorType,
|
|
53
|
+
segments: glazedSegments,
|
|
54
|
+
contentPadding: [0.04, 0.05],
|
|
55
|
+
};
|
|
56
|
+
}
|
package/dist/index.d.ts
CHANGED
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,EAkBf,MAAM,kBAAkB,CAAA;AAIzB,OAAO,EAAE,KAAK,kCAAkC,EAA+B,MAAM,WAAW,CAAA;AAIhG,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,OAAO,CAAC,CAAA;CACtC;AAixBD,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,CA8nD3B"}
|