2026-04-15 21:15:27 +01:00
# ifcurl
**ifcurl** is a command-line tool and Python library for resolving `ifc://` URLs — a URL scheme for addressing a specific view of an IFC building model stored in a git repository.
A single URL encodes the model source (git host, repository, ref), an optional element selection, and an optional camera viewpoint. ifcurl fetches the model and renders a PNG, the way curl fetches a web resource.
```
ifcurl render "ifc://github.com/brunopostle/simple-ifc@heads/main ?path=_test_simple.ifc"
```
---
## URL format
```
ifc://[user@]host/org/repo@< ref > ?< parameters >
```
### Transport
Transport is inferred from the URL structure — no prefix required:
| URL form | Transport |
|---|---|
| `ifc://host/org/repo` | HTTPS |
| `ifc://git@host/org/repo` | SSH |
| `ifc:///path/to/repo` | Local file |
### Ref
| Form | Meaning |
|---|---|
| `@heads/main` | Branch tip |
| `@tags/v1.2` | Tag |
| `@abc123def` | Commit hash |
| `@HEAD` | Default branch |
### Parameters
| Parameter | Description |
|---|---|
| `path=` | Path to the IFC file within the repository |
| `selector=` | [IfcOpenShell selector ](https://docs.ifcopenshell.org/ifcopenshell-python/selector_syntax.html ) expression to filter elements |
| `camera=px,py,pz,dx,dy,dz,ux,uy,uz` | Camera position, view direction, and up vector in IFC world coordinates |
| `fov=60` | Perspective field of view in degrees (use with `camera` ) |
| `scale=50` | Orthographic view-to-world scale (use with `camera` ) |
| `clip=px,py,pz,nx,ny,nz` | Clipping plane — repeatable |
| `visibility=` | `highlight` (default), `ghost` , or `isolate` |
### Visibility modes
| Mode | Behaviour |
|---|---|
| `highlight` | All elements shown; selected elements shown in highlight colour |
| `ghost` | Selected elements shown normally; all others dimmed |
| `isolate` | Only selected elements shown |
---
## Examples
Open a model at the default isometric view:
```
ifcurl render "ifc://github.com/brunopostle/simple-ifc@heads/main ?path=_test_simple.ifc"
```
Walls only, ghost mode:
```
ifcurl render "ifc://github.com/brunopostle/simple-ifc@heads/main ?path=_test_simple.ifc& selector=IfcWall& visibility=ghost"
```
Perspective view with a clipping plane (section cut at z=3):
```
ifcurl render "ifc://example.com/org/project@abc123def ?path=models/building.ifc& camera=10,20,5,0,-1,0,0,0,1& fov=60& clip=0,0,3,0,0,-1"
```
Orthographic plan view from above:
```
ifcurl render "ifc://example.com/org/project@tags/v2 .0?path=models/building.ifc& camera=0,0,8,0,0,-1,0,1,0& scale=50"
```
Local repository:
```
ifcurl render "ifc:///home/alice/projects/office@HEAD ?path=model.ifc"
```
Save to a specific file:
```
ifcurl render "ifc://..." -o output.png
```
---
## Installation
```bash
2026-04-15 23:12:28 +01:00
pip install "ifcurl[render]" # CLI rendering tool
pip install "ifcurl[service]" # preview service (includes render)
2026-04-15 21:15:27 +01:00
```
**Dependencies:**
- [ifcopenshell ](https://ifcopenshell.org ) — IFC parsing and selector execution
- [GitPython ](https://gitpython.readthedocs.io ) — git repository access
2026-04-15 23:12:28 +01:00
- [platformdirs ](https://platformdirs.readthedocs.io ) — OS-appropriate cache and config directories
- [pyvista ](https://pyvista.org ) + [numpy ](https://numpy.org ) — 3D rendering (`[render]` and `[service]` extras)
- [FastAPI ](https://fastapi.tiangolo.com ) + [uvicorn ](https://www.uvicorn.org ) — HTTP service (`[service]` extra)
2026-04-15 21:15:27 +01:00
---
## Python library
```python
2026-04-15 23:12:28 +01:00
from ifcurl import IfcUrl, fetch_ifc
from ifcurl import render as render_mod
2026-04-15 21:15:27 +01:00
import ifcopenshell
import tempfile, os
2026-04-15 23:12:28 +01:00
url = IfcUrl.parse(
"ifc://github.com/brunopostle/simple-ifc@heads/main "
"?path=_test_simple.ifc& selector=IfcWall& visibility=ghost"
)
2026-04-15 21:15:27 +01:00
2026-04-15 23:12:28 +01:00
# fetch_ifc returns (commit_hexsha, ifc_bytes) — the hexsha is useful
# as a cache key even when the URL uses a mutable ref like a branch
hexsha, ifc_bytes = fetch_ifc(url)
2026-04-15 21:15:27 +01:00
tmp_fd, tmp_path = tempfile.mkstemp(suffix=".ifc")
os.write(tmp_fd, ifc_bytes)
os.close(tmp_fd)
model = ifcopenshell.open(tmp_path)
os.unlink(tmp_path)
2026-04-15 23:12:28 +01:00
png_bytes = render_mod.render(
2026-04-15 21:15:27 +01:00
model,
selector=url.selector,
camera=url.camera,
fov=url.fov,
scale=url.scale,
clips=url.clips or None,
visibility=url.visibility,
)
with open("output.png", "wb") as f:
f.write(png_bytes)
```
---
2026-04-15 21:46:04 +01:00
## Preview service
ifcurl includes an HTTP preview service for use by Gitea and other consumers.
```bash
ifcurl serve # 127.0.0.1:8000
ifcurl serve --host 0.0.0.0 --port 9000
```
### Endpoint
```
POST /preview
Content-Type: application/json
2026-04-15 23:12:28 +01:00
{"url": "ifc://...", "token": "optional-git-token"}
2026-04-15 21:46:04 +01:00
```
Returns `image/png` .
2026-04-15 23:12:28 +01:00
The optional `token` field is a bearer token for git authentication. When provided it takes precedence over any token in the config file. Intended for co-located Gitea deployments that pass the requesting user's session token.
### Authentication
For private repositories, configure a token per host in `~/.config/ifcurl/tokens.json` (Linux/macOS) or `%APPDATA%\ifcurl\tokens.json` (Windows):
```json
{
"hosts": {
"github.com": "ghp_your_token_here",
"gitlab.example.com": "glpat_your_token_here"
}
}
```
Tokens are injected into HTTPS remote URLs (`https://<token>@host/path` ) for clone and fetch operations and are never written to the on-disk git config. SSH transport uses the platform key store and ignores this config.
2026-04-15 21:46:04 +01:00
### Service caching
| Tier | Key | Contents | Notes |
|---|---|---|---|
| 2 | commit hash + path | IFC bytes | In-memory LRU, avoids repeated git blob reads |
| 3 | commit hash + path + selector | Resolved GlobalId set | In-memory LRU, avoids re-running selector |
| 4 | SHA-256 of full URL | Rendered PNG | Filesystem, immutable refs only |
Tier 4 is never written for mutable refs (`@heads/` , `@HEAD` ) since the underlying commit may change.
---
2026-04-15 21:15:27 +01:00
## Remote repository caching
Remote repositories are cloned as bare repos to the OS cache directory (`~/.cache/ifcurl/` on Linux, `~/Library/Caches/ifcurl/` on macOS, `%LOCALAPPDATA%\ifcurl\Cache\` on Windows) on first use. Subsequent calls to the same repository reuse the cache. Mutable refs (`@heads/` , `@HEAD` ) trigger a `git fetch` to pick up upstream changes; immutable refs (commit hashes, tags) use the cache as-is.
---
## Development
```bash
git clone https://github.com/brunopostle/ifcurl
cd ifcurl
2026-04-15 23:12:28 +01:00
pip install -e ".[service]"
2026-04-15 21:15:27 +01:00
python -m pytest tests/
```
---
## Licence
LGPL-3.0-or-later