helenkwok
### Problem or motivation Pascal imports IFC through `@pascal-app/ifc-converter` (`convertIfcToPascal`). There is no writer in the other direction. A search of this repository finds no `exportIfc`, `toIfc`, or other Pascal-to-IFC export. The MCP tools export JSON and GLB (`export_json`, `export_glb`). #219 (".ifc bim format support") was closed on 2026-07-19 after import landed. A model that can be read into Pascal still cannot be written back out as IFC. ### Proposed solution Add a writer for a bounded IFC4 subset, in metres: - site, building, and storey - wall centreline, thickness, and height - door and window, carried by `IfcRelVoidsElement` and `IfcRelFillsElement` (overall width and height only; no separate opening mesh) - space Curtain-wall configuration stays a recorded loss. Import already leaves curtain entities as `imported-mesh`, and a written wall should not pretend to restore `wall.curtainWall`. The plan mapping has to match the contract already pinned on `main` by `packages/ifc-converter/tests/handedness.test.ts`, from #931 (merged 2026-09-29): > Pascal is Y-up and right-handed; IFC is Z-up and right-handed. Seen from above, IFC north (+Y) is Pascal -Z, so an IFC plan point (X, Y) is the Pascal plan point (x, z) = (X, -Y). Mapping +Y to +Z mirrors the model. `worldToScene` uses `planDepthSign = -1` when `swapYZ` is on, so a writer for current `main` stores IFC Y as −Pascal z. `@pascal-app/[email protected]` (published 2026-09-24) copies IFC Y without that negation. A writer aimed at 1.0.3 mirrors on current `main`. This request targets `main`. It is not a report that #931 is wrong. If this subset is what you want, I will re-run a small synthetic room against current `main` and then open a pull request in `packages/ifc-converter`, with a bun test that writes the room and reads it back through `convertIfcToPascal`, using the same `(x, z) = (X, -Y)` helper as `handedness.test.ts` and the existing `packages/ifc-converter/tests/fixtures/handedness.ifc` fixture on the import side. ### Alternatives considered - A raw STEP writer aimed at npm 1.0.3. That sign is the opposite of `main`. - A plugin. Plugins add node kinds and sidebar panels. An exporter does not fit that contract. - Treating the existing JSON or GLB export as the IFC path. Those are already there, and they are not IFC. ### Additional context The room I would use for the test, in Pascal plan metres `[x, z]`: - storey at elevation 0, wall height 2.8 m except the extra wall below - south (0, 0) → (6, 0), east (6, 0) → (6, 4), north (6, 4) → (0, 4), west (0, 4) → (0, 0), thickness 0.2 m - one extra wall (0, −1.5) → (3, −1.5), thickness 0.15 m, height 3 m, marked curtain; the curtain configuration is the expected loss - door on the south wall, 0.9 m wide, 2.1 m high, sill 0, starting 2 m along the wall - window on the east wall, 1.2 m wide, 1.1 m high, sill 0.9 m, starting 1.5 m along the wall - one space from (0.1, 0.1) to (5.9, 3.9), ceiling 2.8 m On 1.0.3, that room round-trips centreline, thickness, height, opening size, sill, and the space polygon when IFC Y is written equal to Pascal z. The same file with IFC Y = −Pascal z mirrors. I have not re-run it on `main` (`ec0dcea440`) yet. That re-run happens before any pull request.