# ifcurl **ifcurl** is a URL scheme, tools, and server integration for addressing shareable views of IFC building models stored in git repositories. ``` ifc://github.com/brunopostle/simple-ifc@heads/main?path=building.ifc&selector=IfcWall&fov=60 ``` An `ifc://` URL encodes everything needed to reproduce a specific model view: the git source, the file, which elements to show, and the camera position. Like a permalink for BIM — paste it, share it, embed it in documentation. --- ## Architecture Four interlocking components: | Component | Language | What it does | |---|---|---| | **Python library + CLI** | Python | Parse ifc:// URLs, fetch IFC from git, render PNG | | **Preview service** | Python | HTTP server — PNGs on demand, IFC file proxy | | **Forgejo integration** | Go + assets | Inline markdown previews, "View in 3D" button | | **Browser viewer** | JavaScript | Interactive WebGL viewer, served as a Forgejo asset | ``` Markdown source: [Section cut](ifc://host/org/repo@heads/main?path=building.ifc&clip=0,0,3,0,0,-1) │ Forgejo renders (via Go patch): │
──────┤── preview service renders PNG browser viewer (WebGL, interactive)
``` The viewer is also reachable directly — paste or type an `ifc://` URL into its toolbar, or drag one onto the page. --- ## ifc:// URL scheme Full specification: [`SPECIFICATION.md`](SPECIFICATION.md) ``` ifc://[user@]host/org/repo@? ``` Transport is inferred from the URL structure — no prefix needed: | URL form | Transport | |---|---| | `ifc://host/org/repo` | HTTPS | | `ifc://git@host/org/repo` | SSH | | `ifc:///path/to/repo` | Local file | Refs follow git namespace form to avoid branch/tag ambiguity: | Form | Meaning | |---|---| | `@heads/main` | Branch tip | | `@tags/v1.2` | Tag | | `@abc123def` | Commit hash | | `@HEAD` | Default branch | Key query parameters: | Parameter | Description | |---|---| | `path=` | IFC file path within the repository | | `selector=IfcWall` | [IfcOpenShell selector](https://docs.ifcopenshell.org/ifcopenshell-python/selector_syntax.html) — filter elements; `+` for union | | `camera=px,py,pz,dx,dy,dz,ux,uy,uz` | Camera position, direction, up in IFC world coordinates | | `fov=60` | Perspective field of view in degrees | | `scale=50` | Orthographic view-to-world scale | | `clip=px,py,pz,nx,ny,nz` | Clipping plane — point + normal in IFC coords (repeatable) | | `visibility=` | `highlight` · `ghost` · `isolate` | --- ## Python library and CLI Fetch an IFC file from a git repository and render it to PNG. ```bash pip install "ifcurl[render]" ifcurl render "ifc://github.com/brunopostle/simple-ifc@heads/main?path=_test_simple.ifc" ifcurl render "ifc://..." -o output.png ``` Use as a library: ```python from ifcurl import IfcUrl, fetch_ifc from ifcurl.render import render url = IfcUrl.parse( "ifc://example.com/org/repo@heads/main" "?path=models/building.ifc&selector=IfcWall&visibility=ghost" ) hexsha, ifc_bytes = fetch_ifc(url) # hexsha is stable even for mutable refs model = ifcopenshell.open(...) # write ifc_bytes to tmp file first png = render(model, selector=url.selector, clips=url.clips or None, camera=url.camera, fov=url.fov, visibility=url.visibility) ``` Remote repositories are cloned as bare repos to the OS cache directory (`~/.cache/ifcurl/` on Linux) on first use. Mutable refs (`@heads/`, `@HEAD`) trigger a `git fetch`; immutable refs (commit hashes, tags) use the cache as-is. For private repositories, configure tokens per host in `~/.config/ifcurl/tokens.json`: ```json { "hosts": { "github.com": "ghp_…", "gitlab.example.com": "glpat_…" } } ``` --- ## Preview service An HTTP service that renders `ifc://` URLs to PNG, intended for co-location with a Forgejo/Gitea instance. ```bash pip install "ifcurl[service]" ifcurl serve # 127.0.0.1:8000 ifcurl serve --host 0.0.0.0 --port 9000 ``` ### Endpoints | Endpoint | Description | |---|---| | `POST /preview` | Render an ifc:// URL to PNG | | `GET /preview?url=ifc://…` | Same, via query string | | `GET /viewer?url=ifc://…` | Redirect to the Forgejo browser viewer | | `GET /proxy?url=` | Proxy an IFC file (CORS workaround) | ### Caching | Tier | Key | Contents | Notes | |---|---|---|---| | 2 | commit hash + path | IFC bytes | In-memory LRU | | 3 | commit hash + path + selector | Resolved element set | In-memory LRU | | 4 | SHA-256 of full URL | Rendered PNG | Filesystem; immutable refs only | Tier 4 is never written for mutable refs (`@heads/`, `@HEAD`). --- ## Forgejo integration A source patch for Forgejo that adds: - **Inline markdown preview** — `[label](ifc://…)` and bare `ifc://…` in markdown render as `
` preview images linked to the browser viewer. - **"View in 3D" button** — appears on `.ifc` file view pages alongside Raw / Permalink / History. - **Browser viewer asset** — `viewer.html` + `viewer-url.js` served at `/assets/viewer.html`. See [`forgejo/README.md`](forgejo/README.md) for full apply, build, and deployment instructions. ### Quick summary ```bash # Apply the Go source patch cp forgejo/modules/markup/markdown/ifc_url{,_test}.go /path/to/forgejo/modules/markup/markdown/ cd /path/to/forgejo && git apply /path/to/ifcurl/forgejo/go.patch # Build Forgejo go build -tags 'sqlite sqlite_unlock_notify' \ -ldflags "-X 'forgejo.org/modules/setting.StaticRootPath=/usr/share/forgejo'" \ -o forgejo . && sudo cp forgejo /usr/bin/forgejo # Deploy assets (no rebuild needed) sudo cp forgejo/custom/public/assets/viewer*.* /etc/forgejo/public/assets/ sudo cp forgejo/templates/custom/footer.tmpl /var/lib/forgejo/custom/templates/custom/ sudo systemctl restart forgejo ``` Add to `/etc/forgejo/conf/app.ini`: ```ini [ifcurl] PREVIEW_SERVICE_URL = http://localhost:8000 ``` --- ## Browser viewer A self-contained WebGL IFC viewer (`viewer.html`) built on [`@thatopen/components`](https://github.com/ThatOpenCompany/that-open-engine). No build step — loads dependencies from CDN. **Features:** - Toolbar with raw ifc:// URL input and structured fields (repo, ref, path, selector) — editing any field reloads the model - Selector filtering — `IfcWall`, `IfcWall+IfcSlab`, etc. - Clipping planes — `✂ clip` button, double-click on model surface to place; drag handles to adjust; planes serialised back into the URL - FOV control, camera sync — the URL in the browser bar always reflects the current view and is shareable - Drag-and-drop ifc:// URLs onto the page --- ## Development ```bash git clone https://github.com/brunopostle/ifcurl cd ifcurl pip install -e ".[service]" # Python tests python -m pytest tests/ # JavaScript tests (Node 18+) node --test tests/test_viewer_url.mjs # Go tests (requires Forgejo source tree with patch applied) cd /path/to/forgejo go test ./modules/markup/markdown/ -run TestIfcURL -v ``` ### Roadmap | Phase | Status | Description | |---|---|---| | 1 — Python core | ✓ done | URL parsing, git fetch, render | | 2 — Preview service | ✓ done | HTTP service with caching | | 3 — Forgejo integration | ✓ done | Go patch, viewer, markdown extension | | 4 — Bonsai integration | planned | Protocol handler + "Copy view URL" | | 5 — IFC Viewer integration | planned | Plugin for the open-source IFC Viewer | | 6 — Federation | planned | `IFCDOCUMENTREFERENCE` cross-repo links | Development tasks are tracked with [beads](https://github.com/brunopostle/beads): `bd ready`. --- ## Licence LGPL-3.0-or-later